You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by pa...@apache.org on 2026/10/19 02:48:25 UTC

(lucenenet-codeanalysis-dev) branch main updated: Add TestApiConsistency analyzers for #1359 (#44)

This is an automated email from the ASF dual-hosted git repository.

paulirwin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/lucenenet-codeanalysis-dev.git


The following commit(s) were added to refs/heads/main by this push:
     new b298fbb  Add TestApiConsistency analyzers for #1359 (#44)
b298fbb is described below

commit b298fbb41eeb4c1a37bb4592725bb6480c2a9490
Author: Paul Irwin <pa...@gmail.com>
AuthorDate: Sun Oct 18 20:48:20 2026 -0600

    Add TestApiConsistency analyzers for #1359 (#44)
    
    * Add TestApiConsistency analyzers for #1359
    
    Port the LUCENENET TestApiConsistency (ApiScanTestBase) rules from unit
    tests into Dev analyzers so violations surface in the IDE and at build
    time. Adds 16 analyzers:
    
    Naming (LuceneDev7000-LuceneDev7008): private/protected field names,
    method parameter names, interface names, class names (incl. delegates),
    members containing 'Comparator', members named 'Size', and member/type
    names containing non-.NET numeric terms.
    
    Design (LuceneDev1009-LuceneDev1015): public fields, setter-only
    properties, properties returning arrays, methods returning writable
    arrays, nullable enum members, IEnumerable members (disabled by default,
    a one-time port aid), and List/Dictionary members.
    
    Rename code fixes are provided for the protected-field ('m_' prefix) and
    interface ('I' prefix) naming rules. Behavior mirrors the original
    reflection-based tests, including the LUCENENET suppression attributes
    ([WritableArray], [ExceptionToClassNameConvention], etc.) matched by
    name. LuceneDev1012 reproduces the original IL 'returns a field directly'
    check via method-body syntax analysis. Includes samples and unit tests
    for all rules.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <no...@anthropic.com>
    
    * Address PR review feedback for #1359
    
    - LuceneDev1012: detect nullable-annotated array return types (e.g. int[]?)
    - LuceneDev7003 code fix: offer rename for wrong-case 'I' prefix (e.g. iFoo -> IFoo)
    - LuceneDev7005: include explicit interface implementations, matching the
      original reflection-based ApiScanTestBase scan
    
    Co-Authored-By: Claude Opus 5.5 <no...@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 .../CodeFixResources.resx                          |   4 +
 ...eneDev7001_ProtectedFieldNameCodeFixProvider.cs |  82 ++++++++++
 .../LuceneDev7003_InterfaceNameCodeFixProvider.cs  |  82 ++++++++++
 .../Utility/FieldRenameHelper.cs                   |  74 +++++++++
 .../LuceneDev1009Sample.cs                         |  26 +++
 .../LuceneDev1010Sample.cs                         |  31 ++++
 .../LuceneDev1011Sample.cs                         |  28 ++++
 .../LuceneDev1012Sample.cs                         |  31 ++++
 .../LuceneDev1013Sample.cs                         |  32 ++++
 .../LuceneDev1014Sample.cs                         |  31 ++++
 .../LuceneDev1015Sample.cs                         |  31 ++++
 .../LuceneDev7000Sample.cs                         |  28 ++++
 .../LuceneDev7001Sample.cs                         |  28 ++++
 .../LuceneDev7002Sample.cs                         |  26 +++
 .../LuceneDev7003Sample.cs                         |  26 +++
 .../LuceneDev7004Sample.cs                         |  26 +++
 .../LuceneDev7005Sample.cs                         |  26 +++
 .../LuceneDev7006Sample.cs                         |  26 +++
 .../LuceneDev7007Sample.cs                         |  26 +++
 .../LuceneDev7008Sample.cs                         |  26 +++
 .../AnalyzerReleases.Unshipped.md                  |  16 ++
 .../LuceneDev1009_PublicFieldsAnalyzer.cs          |  83 ++++++++++
 .../LuceneDev1010_PropertyWithNoGetterAnalyzer.cs  |  75 +++++++++
 .../LuceneDev1011_PropertyReturnsArrayAnalyzer.cs  |  80 ++++++++++
 ...neDev1012_MethodReturnsWritableArrayAnalyzer.cs | 127 +++++++++++++++
 .../LuceneDev1013_NullableEnumAnalyzer.cs          | 158 ++++++++++++++++++
 ...14_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs |  44 ++++++
 ...mberAcceptsOrReturnsListOrDictionaryAnalyzer.cs |  58 +++++++
 .../LuceneDev1xxx/MembersUsingTypeAnalyzerBase.cs  | 141 +++++++++++++++++
 .../LuceneDev7000_PrivateFieldNameAnalyzer.cs      |  76 +++++++++
 .../LuceneDev7001_ProtectedFieldNameAnalyzer.cs    |  87 ++++++++++
 .../LuceneDev7002_MethodParameterNameAnalyzer.cs   |  86 ++++++++++
 .../LuceneDev7003_InterfaceNameAnalyzer.cs         |  69 ++++++++
 .../LuceneDev7004_ClassNameAnalyzer.cs             |  78 +++++++++
 ...LuceneDev7005_MemberContainsComparerAnalyzer.cs | 140 ++++++++++++++++
 .../LuceneDev7006_MemberNamedSizeAnalyzer.cs       |  49 ++++++
 ...eDev7007_MemberContainsNonNetNumericAnalyzer.cs |  46 ++++++
 ...eneDev7008_TypeContainsNonNetNumericAnalyzer.cs |  68 ++++++++
 .../MemberNamePredicateAnalyzerBase.cs             | 143 +++++++++++++++++
 src/Lucene.Net.CodeAnalysis.Dev/Resources.resx     | 176 +++++++++++++++++++++
 .../Utility/ApiConventionHelper.cs                 |  79 +++++++++
 .../Utility/Descriptors.LuceneDev1xxx.cs           |  52 ++++++
 ...ceneDev1xxx.cs => Descriptors.LuceneDev7xxx.cs} |  56 +++----
 .../Utility/FieldNameHelper.cs                     |  64 ++++++++
 .../Utility/NamingHelper.cs                        |  62 ++++++++
 ...eneDev7001_ProtectedFieldNameCodeFixProvider.cs |  69 ++++++++
 ...stLuceneDev7003_InterfaceNameCodeFixProvider.cs | 121 ++++++++++++++
 .../TestLuceneDev1009_PublicFieldsAnalyzer.cs      | 107 +++++++++++++
 ...stLuceneDev1010_PropertyWithNoGetterAnalyzer.cs |  98 ++++++++++++
 ...stLuceneDev1011_PropertyReturnsArrayAnalyzer.cs |  72 +++++++++
 ...neDev1012_MethodReturnsWritableArrayAnalyzer.cs | 146 +++++++++++++++++
 .../TestLuceneDev1013_NullableEnumAnalyzer.cs      | 121 ++++++++++++++
 ...14_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs | 100 ++++++++++++
 ...mberAcceptsOrReturnsListOrDictionaryAnalyzer.cs | 138 ++++++++++++++++
 .../TestLuceneDev7000_PrivateFieldNameAnalyzer.cs  | 126 +++++++++++++++
 ...TestLuceneDev7001_ProtectedFieldNameAnalyzer.cs | 136 ++++++++++++++++
 ...estLuceneDev7002_MethodParameterNameAnalyzer.cs |  95 +++++++++++
 .../TestLuceneDev7003_InterfaceNameAnalyzer.cs     |  91 +++++++++++
 .../TestLuceneDev7004_ClassNameAnalyzer.cs         | 129 +++++++++++++++
 ...LuceneDev7005_MemberContainsComparerAnalyzer.cs | 158 ++++++++++++++++++
 .../TestLuceneDev7006_MemberNamedSizeAnalyzer.cs   | 113 +++++++++++++
 ...eDev7007_MemberContainsNonNetNumericAnalyzer.cs |  78 +++++++++
 ...eneDev7008_TypeContainsNonNetNumericAnalyzer.cs |  69 ++++++++
 63 files changed, 4742 insertions(+), 28 deletions(-)

diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx
index 0c5634c..878bb89 100644
--- a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx
+++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx
@@ -125,4 +125,8 @@ under the License.
   <data name="MakeXInternal" xml:space="preserve">
     <value>Make {0} internal</value>
   </data>
+  <data name="RenameToX" xml:space="preserve">
+    <value>Rename to '{0}'</value>
+    <comment>Title for a rename code fix; {0} is the suggested new name.</comment>
+  </data>
 </root>
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev7xxx/LuceneDev7001_ProtectedFieldNameCodeFixProvider.cs b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev7xxx/LuceneDev7001_ProtectedFieldNameCodeFixProvider.cs
new file mode 100644
index 0000000..faa8248
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev7xxx/LuceneDev7001_ProtectedFieldNameCodeFixProvider.cs
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.CodeFixes;
+using Lucene.Net.CodeAnalysis.Dev.CodeFixes.Utility;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.Rename;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+/// <summary>
+/// Code fix for <see cref="Descriptors.LuceneDev7001_ProtectedFieldName"/>: renames a protected
+/// field to the conventional 'm_' prefix followed by camelCase.
+/// </summary>
+[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(LuceneDev7001_ProtectedFieldNameCodeFixProvider)), Shared]
+public class LuceneDev7001_ProtectedFieldNameCodeFixProvider : CodeFixProvider
+{
+    public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
+        [Descriptors.LuceneDev7001_ProtectedFieldName.Id];
+
+    // Renames touch the whole solution; the batch fixer is not appropriate.
+    public sealed override FixAllProvider? GetFixAllProvider() => null;
+
+    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
+    {
+        var diagnostic = context.Diagnostics.First();
+        var diagnosticSpan = diagnostic.Location.SourceSpan;
+
+        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+        var node = root?.FindNode(diagnosticSpan);
+        if (node is null)
+            return;
+
+        var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
+        if (semanticModel is null)
+            return;
+
+        var symbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken);
+        if (symbol is not IFieldSymbol fieldSymbol)
+            return;
+
+        var newName = FieldRenameHelper.ToProtectedFieldName(fieldSymbol.Name);
+        if (newName is null || newName == fieldSymbol.Name)
+            return;
+
+        context.RegisterCodeFix(
+            CodeActionHelper.CreateFromResource(
+                CodeFixResources.RenameToX,
+                createChangedSolution: c => RenameAsync(context.Document, fieldSymbol, newName, c),
+                $"RenameProtectedField_{newName}",
+                newName),
+            diagnostic);
+    }
+
+    private static async Task<Solution> RenameAsync(Document document, ISymbol symbol, string newName, CancellationToken cancellationToken)
+    {
+        var solution = document.Project.Solution;
+        var options = new SymbolRenameOptions();
+        return await Renamer.RenameSymbolAsync(solution, symbol, options, newName, cancellationToken).ConfigureAwait(false);
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev7xxx/LuceneDev7003_InterfaceNameCodeFixProvider.cs b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev7xxx/LuceneDev7003_InterfaceNameCodeFixProvider.cs
new file mode 100644
index 0000000..06e8bca
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev7xxx/LuceneDev7003_InterfaceNameCodeFixProvider.cs
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.CodeFixes;
+using Lucene.Net.CodeAnalysis.Dev.CodeFixes.Utility;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.Rename;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+/// <summary>
+/// Code fix for <see cref="Descriptors.LuceneDev7003_InterfaceName"/>: renames an interface to
+/// begin with a capital 'I'.
+/// </summary>
+[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(LuceneDev7003_InterfaceNameCodeFixProvider)), Shared]
+public class LuceneDev7003_InterfaceNameCodeFixProvider : CodeFixProvider
+{
+    public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
+        [Descriptors.LuceneDev7003_InterfaceName.Id];
+
+    // Renames touch the whole solution; the batch fixer is not appropriate.
+    public sealed override FixAllProvider? GetFixAllProvider() => null;
+
+    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
+    {
+        var diagnostic = context.Diagnostics.First();
+        var diagnosticSpan = diagnostic.Location.SourceSpan;
+
+        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+        var node = root?.FindNode(diagnosticSpan);
+        if (node is null)
+            return;
+
+        var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
+        if (semanticModel is null)
+            return;
+
+        var symbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken);
+        if (symbol is not INamedTypeSymbol typeSymbol || typeSymbol.TypeKind != TypeKind.Interface)
+            return;
+
+        var newName = FieldRenameHelper.ToInterfaceName(typeSymbol.Name);
+        if (newName is null || newName == typeSymbol.Name)
+            return;
+
+        context.RegisterCodeFix(
+            CodeActionHelper.CreateFromResource(
+                CodeFixResources.RenameToX,
+                createChangedSolution: c => RenameAsync(context.Document, typeSymbol, newName, c),
+                $"RenameInterface_{newName}",
+                newName),
+            diagnostic);
+    }
+
+    private static async Task<Solution> RenameAsync(Document document, ISymbol symbol, string newName, CancellationToken cancellationToken)
+    {
+        var solution = document.Project.Solution;
+        var options = new SymbolRenameOptions();
+        return await Renamer.RenameSymbolAsync(solution, symbol, options, newName, cancellationToken).ConfigureAwait(false);
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Utility/FieldRenameHelper.cs b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Utility/FieldRenameHelper.cs
new file mode 100644
index 0000000..8f7b239
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Utility/FieldRenameHelper.cs
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Globalization;
+
+namespace Lucene.Net.CodeAnalysis.Dev.CodeFixes.Utility;
+
+/// <summary>
+/// Helpers that compute conventional names used by the naming code fixes.
+/// </summary>
+internal static class FieldRenameHelper
+{
+    /// <summary>
+    /// Computes the conventional protected field name ('m_' prefix followed by camelCase) for the
+    /// given field name, or <c>null</c> when a sensible suggestion cannot be produced (e.g. the name
+    /// is an UPPER_CASE constant, which is already an allowed form).
+    /// </summary>
+    public static string? ToProtectedFieldName(string name)
+    {
+        if (string.IsNullOrEmpty(name))
+            return null;
+
+        // Strip any leading underscores and a leading 'm_' if already present.
+        var core = name.TrimStart('_');
+        if (core.StartsWith("m_", System.StringComparison.Ordinal))
+            core = core.Substring(2);
+
+        core = core.TrimStart('_');
+        if (core.Length == 0)
+            return null;
+
+        // camelCase the first letter.
+        var camel = char.ToLower(core[0], CultureInfo.InvariantCulture) + core.Substring(1);
+        return "m_" + camel;
+    }
+
+    /// <summary>
+    /// Computes the conventional interface name (capital 'I' prefix followed by PascalCase) for the
+    /// given interface name, or <c>null</c> when a sensible suggestion cannot be produced.
+    /// </summary>
+    public static string? ToInterfaceName(string name)
+    {
+        if (string.IsNullOrEmpty(name))
+            return null;
+
+        // Drop leading underscores, then PascalCase the first letter.
+        var core = name.TrimStart('_');
+        if (core.Length == 0)
+            return null;
+
+        var pascal = char.ToUpper(core[0], CultureInfo.InvariantCulture) + core.Substring(1);
+
+        // If it already starts with 'I' followed by an uppercase letter, the prefix is present and
+        // only the case or leading underscores needed fixing (e.g. 'iFoo' or '_IFoo' -> 'IFoo').
+        if (pascal.Length >= 2 && pascal[0] == 'I' && char.IsUpper(pascal[1]))
+            return pascal == name ? null : pascal;
+
+        return "I" + pascal;
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1009Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1009Sample.cs
new file mode 100644
index 0000000..58d77a7
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1009Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev1009Sample
+{
+    // VIOLATION: public instance field should be a property instead.
+    public int Value;
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1010Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1010Sample.cs
new file mode 100644
index 0000000..e2dc34c
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1010Sample.cs
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev1010Sample
+{
+    private int _value;
+
+    // VIOLATION: property has a setter but no getter.
+    public int Value
+    {
+        set => _value = value;
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1011Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1011Sample.cs
new file mode 100644
index 0000000..b9d8e5c
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1011Sample.cs
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev1011Sample
+{
+    private readonly int[] _values = new int[4];
+
+    // VIOLATION: property returns an array.
+    public int[] Values => _values;
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1012Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1012Sample.cs
new file mode 100644
index 0000000..ada2bcd
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1012Sample.cs
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev1012Sample
+{
+    private readonly int[] m_values = new int[4];
+
+    // VIOLATION: method returns the backing array directly, so callers can mutate it.
+    public int[] GetValues()
+    {
+        return m_values;
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1013Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1013Sample.cs
new file mode 100644
index 0000000..463c872
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1013Sample.cs
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public enum LuceneDev1013Mode
+{
+    First,
+    Second
+}
+
+public class LuceneDev1013Sample
+{
+    // VIOLATION: public member that is a nullable enum.
+    public LuceneDev1013Mode? Mode { get; set; }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1014Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1014Sample.cs
new file mode 100644
index 0000000..926ae60
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1014Sample.cs
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Generic;
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev1014Sample
+{
+    // VIOLATION (rule disabled by default): member accepts or returns IEnumerable<T>.
+    public IEnumerable<int> GetItems(IEnumerable<int> source)
+    {
+        return source;
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1015Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1015Sample.cs
new file mode 100644
index 0000000..6923d27
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1015Sample.cs
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Generic;
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev1015Sample
+{
+    // VIOLATION: member returns the concrete List<T> instead of IList<T>.
+    public List<int> GetItems()
+    {
+        return new List<int>();
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7000Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7000Sample.cs
new file mode 100644
index 0000000..fe98d9d
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7000Sample.cs
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev7000Sample
+{
+    // VIOLATION: private field is not camelCase (PascalCase is not allowed).
+    private int FieldValue = 0;
+
+    public int GetValue() => FieldValue;
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7001Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7001Sample.cs
new file mode 100644
index 0000000..ba5f285
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7001Sample.cs
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev7001Sample
+{
+    // VIOLATION: protected field should use the 'm_' prefix (e.g. m_count).
+    protected int count;
+
+    protected LuceneDev7001Sample(int value) => count = value;
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7002Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7002Sample.cs
new file mode 100644
index 0000000..ce134e4
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7002Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev7002Sample
+{
+    // VIOLATION: method parameter is not camelCase (PascalCase is not allowed).
+    public int Add(int First, int second) => First + second;
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7003Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7003Sample.cs
new file mode 100644
index 0000000..ee97665
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7003Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+// VIOLATION: interface name does not begin with 'I'.
+public interface LuceneDev7003Sample
+{
+    void DoWork();
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7004Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7004Sample.cs
new file mode 100644
index 0000000..1e5c215
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7004Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+// VIOLATION: class name follows the interface naming convention (capital 'I' + capital letter).
+public class ILuceneDev7004Sample
+{
+    public void DoWork() { }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7005Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7005Sample.cs
new file mode 100644
index 0000000..bae6b66
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7005Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev7005Sample
+{
+    // VIOLATION: member name contains the Java term 'Comparator' (should be 'Comparer').
+    public int CompareWithComparator(int a, int b) => a.CompareTo(b);
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7006Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7006Sample.cs
new file mode 100644
index 0000000..aa86348
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7006Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev7006Sample
+{
+    // VIOLATION: member named 'Size' (should be 'Count' or 'Length').
+    public int Size { get; set; }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7007Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7007Sample.cs
new file mode 100644
index 0000000..20b6468
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7007Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+public class LuceneDev7007Sample
+{
+    // VIOLATION: member name contains the Java numeric term 'Long' (should be 'Int64').
+    public long ReadLong() => 0L;
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7008Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7008Sample.cs
new file mode 100644
index 0000000..4e73d2e
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev7008Sample.cs
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Lucene.Net.CodeAnalysis.Dev.Sample;
+
+// VIOLATION: type name contains the Java numeric term 'Long' (should be 'Int64').
+public class LongValueHolder
+{
+    public long Value { get; set; }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md b/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md
index 4c74a14..b2f23a3 100644
--- a/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md
+++ b/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md
@@ -2,3 +2,19 @@
 
 Rule ID       | Category | Severity | Notes
 --------------|----------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------
+LuceneDev1009 | Design   | Warning  | Fields should not be public; use a public property instead
+LuceneDev1010 | Design   | Warning  | Properties should not have a setter without a getter
+LuceneDev1011 | Design   | Warning  | Properties should not return arrays
+LuceneDev1012 | Design   | Warning  | Methods should not return writable arrays
+LuceneDev1013 | Design   | Warning  | Public members should not be nullable enums
+LuceneDev1014 | Design   | Disabled | Members should not accept or return IEnumerable<T> (one-time port aid; disabled by default)
+LuceneDev1015 | Design   | Warning  | Members should not accept or return List<T> or Dictionary<K, V>; prefer interface types
+LuceneDev7000 | Naming   | Warning  | Private fields should be camelCase, optionally with a leading underscore
+LuceneDev7001 | Naming   | Warning  | Protected fields should use the 'm_' prefix followed by camelCase
+LuceneDev7002 | Naming   | Warning  | Method parameters should be camelCase
+LuceneDev7003 | Naming   | Warning  | Interface names should begin with 'I'
+LuceneDev7004 | Naming   | Warning  | Class names should use PascalCase
+LuceneDev7005 | Naming   | Warning  | Public members should not contain the word 'Comparator'; use 'Comparer'
+LuceneDev7006 | Naming   | Warning  | Public members should not be named 'Size'; use 'Count' or 'Length'
+LuceneDev7007 | Naming   | Warning  | Public members should not contain non-.NET numeric type names (Int/Long/Short/Float)
+LuceneDev7008 | Naming   | Warning  | Type names should not contain non-.NET numeric type names (Int/Long/Short/Float)
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1009_PublicFieldsAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1009_PublicFieldsAnalyzer.cs
new file mode 100644
index 0000000..8110fc0
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1009_PublicFieldsAnalyzer.cs
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestPublicFields: public instance fields of classes are reported;
+    /// consider using a public property instead. Mirrors the original test, which scans only
+    /// instance fields (so const and static fields are not considered) and skips the
+    /// Lucene.Net.Support namespace.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev1009_PublicFieldsAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev1009_PublicFields);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field);
+        }
+
+        private static void AnalyzeField(SymbolAnalysisContext context)
+        {
+            var field = (IFieldSymbol)context.Symbol;
+            var containingType = field.ContainingType;
+
+            if (containingType?.TypeKind != TypeKind.Class)
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(containingType))
+                return;
+
+            if (ApiConventionHelper.IsCompilerGeneratedName(containingType.MetadataName))
+                return;
+
+            if (ApiConventionHelper.IsInSupportNamespace(containingType))
+                return;
+
+            // Original test binds Public | Instance only: static and const fields are not scanned.
+            if (field.IsStatic || field.IsConst)
+                return;
+
+            if (field.IsImplicitlyDeclared || ApiConventionHelper.IsCompilerGeneratedName(field.Name))
+                return;
+
+            if (field.DeclaredAccessibility != Accessibility.Public)
+                return;
+
+            foreach (var location in field.Locations)
+            {
+                if (location.IsInSource)
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(
+                        Descriptors.LuceneDev1009_PublicFields, location, field.Name));
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1010_PropertyWithNoGetterAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1010_PropertyWithNoGetterAnalyzer.cs
new file mode 100644
index 0000000..fb92f0c
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1010_PropertyWithNoGetterAnalyzer.cs
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForPropertiesWithNoGetter: instance properties of classes that
+    /// have a setter but no getter are reported. Mirrors the original test, which considers
+    /// accessors of any accessibility.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev1010_PropertyWithNoGetterAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev1010_PropertyWithNoGetter);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
+        }
+
+        private static void AnalyzeProperty(SymbolAnalysisContext context)
+        {
+            var property = (IPropertySymbol)context.Symbol;
+            var containingType = property.ContainingType;
+
+            if (containingType?.TypeKind != TypeKind.Class)
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(containingType))
+                return;
+
+            // Original test binds instance properties only.
+            if (property.IsStatic)
+                return;
+
+            // Note: the original (reflection GetProperties) includes indexers, so a set-only
+            // indexer is intentionally flagged here as well.
+            if (property.SetMethod != null && property.GetMethod == null)
+            {
+                foreach (var location in property.Locations)
+                {
+                    if (location.IsInSource)
+                    {
+                        context.ReportDiagnostic(Diagnostic.Create(
+                            Descriptors.LuceneDev1010_PropertyWithNoGetter, location, property.Name));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1011_PropertyReturnsArrayAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1011_PropertyReturnsArrayAnalyzer.cs
new file mode 100644
index 0000000..47ae278
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1011_PropertyReturnsArrayAnalyzer.cs
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForPropertiesThatReturnArray: instance properties of classes
+    /// with a public getter that returns an array type are reported. Properties decorated with
+    /// [WritableArray] are excluded, matching the original test.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev1011_PropertyReturnsArrayAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev1011_PropertyReturnsArray);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
+        }
+
+        private static void AnalyzeProperty(SymbolAnalysisContext context)
+        {
+            var property = (IPropertySymbol)context.Symbol;
+            var containingType = property.ContainingType;
+
+            if (containingType?.TypeKind != TypeKind.Class)
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(containingType))
+                return;
+
+            if (property.IsStatic)
+                return;
+
+            if (ApiConventionHelper.HasAttribute(property, ApiConventionHelper.WritableArrayAttribute))
+                return;
+
+            // Original test uses GetGetMethod() (public getter only).
+            var getMethod = property.GetMethod;
+            if (getMethod is null || getMethod.DeclaredAccessibility != Accessibility.Public)
+                return;
+
+            if (property.Type.TypeKind == TypeKind.Array)
+            {
+                foreach (var location in property.Locations)
+                {
+                    if (location.IsInSource)
+                    {
+                        context.ReportDiagnostic(Diagnostic.Create(
+                            Descriptors.LuceneDev1011_PropertyReturnsArray, location, property.Name));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1012_MethodReturnsWritableArrayAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1012_MethodReturnsWritableArrayAnalyzer.cs
new file mode 100644
index 0000000..bcb580a
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1012_MethodReturnsWritableArrayAnalyzer.cs
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForMethodsThatReturnWritableArray. The original test inspected
+    /// the compiled IL to detect a method body that simply returns a field (so the consumer can
+    /// mutate the array). Because a source analyzer has no IL, this analyzer inspects the method
+    /// body syntax and reports when an array-returning method returns a field reference directly,
+    /// without cloning it (e.g. <c>return m_values;</c>). Methods decorated with [WritableArray]
+    /// are excluded, matching the original test.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev1012_MethodReturnsWritableArrayAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev1012_MethodReturnsWritableArray);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSyntaxNodeAction(AnalyzeMethod, SyntaxKind.MethodDeclaration);
+        }
+
+        private static void AnalyzeMethod(SyntaxNodeAnalysisContext context)
+        {
+            var methodDeclaration = (MethodDeclarationSyntax)context.Node;
+
+            // Must be declared on a class.
+            if (methodDeclaration.Parent is not ClassDeclarationSyntax)
+                return;
+
+            // Return type must be an array (possibly nullable-annotated, e.g. int[]?).
+            if (methodDeclaration.ReturnType is not (ArrayTypeSyntax or NullableTypeSyntax { ElementType: ArrayTypeSyntax }))
+                return;
+
+            var semanticModel = context.SemanticModel;
+            var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken);
+            if (methodSymbol is null)
+                return;
+
+            if (methodSymbol.ReturnType.TypeKind != TypeKind.Array)
+                return;
+
+            if (ApiConventionHelper.HasAttribute(methodSymbol, ApiConventionHelper.WritableArrayAttribute))
+                return;
+
+            foreach (var returnedExpression in GetReturnedExpressions(methodDeclaration))
+            {
+                if (ReturnsFieldDirectly(returnedExpression, semanticModel, context.CancellationToken))
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(
+                        Descriptors.LuceneDev1012_MethodReturnsWritableArray,
+                        methodDeclaration.Identifier.GetLocation(),
+                        methodSymbol.Name));
+                    return; // one diagnostic per method
+                }
+            }
+        }
+
+        private static IEnumerable<ExpressionSyntax> GetReturnedExpressions(MethodDeclarationSyntax method)
+        {
+            // Expression-bodied member: e.g. int[] GetValues() => m_values;
+            if (method.ExpressionBody?.Expression is { } arrow)
+            {
+                yield return arrow;
+                yield break;
+            }
+
+            if (method.Body is null)
+                yield break;
+
+            foreach (var statement in method.Body.DescendantNodes().OfType<ReturnStatementSyntax>())
+            {
+                if (statement.Expression is { } expression)
+                {
+                    yield return expression;
+                }
+            }
+        }
+
+        private static bool ReturnsFieldDirectly(ExpressionSyntax expression, SemanticModel semanticModel, System.Threading.CancellationToken cancellationToken)
+        {
+            // Unwrap parentheses.
+            var unwrapped = expression;
+            while (unwrapped is ParenthesizedExpressionSyntax parenthesized)
+            {
+                unwrapped = parenthesized.Expression;
+            }
+
+            // A direct field reference: 'field' or 'this.field'. Anything else (a method call such
+            // as arr.ToArray(), a 'new[]' allocation, etc.) returns a fresh array and is safe.
+            if (unwrapped is not (IdentifierNameSyntax or MemberAccessExpressionSyntax))
+                return false;
+
+            var symbol = semanticModel.GetSymbolInfo(unwrapped, cancellationToken).Symbol;
+            return symbol is IFieldSymbol;
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1013_NullableEnumAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1013_NullableEnumAnalyzer.cs
new file mode 100644
index 0000000..30b4589
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1013_NullableEnumAnalyzer.cs
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForPublicMembersWithNullableEnum: reports non-private methods
+    /// (return value and parameters), non-private constructors (parameters), non-private properties,
+    /// and protected fields whose type is a nullable enum (<c>Nullable&lt;TEnum&gt;</c>). Members
+    /// decorated with [ExceptionToNullableEnumConvention] are excluded, matching the original test.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev1013_NullableEnumAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev1013_NullableEnum);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
+            context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
+            context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field);
+        }
+
+        private static bool IsNullableEnum(ITypeSymbol type)
+        {
+            if (type is INamedTypeSymbol named
+                && named.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T
+                && named.TypeArguments.Length == 1)
+            {
+                return named.TypeArguments[0].TypeKind == TypeKind.Enum;
+            }
+
+            return false;
+        }
+
+        private static bool MemberPreconditions(ISymbol member)
+        {
+            var containingType = member.ContainingType;
+            if (containingType is null || ApiConventionHelper.IsGeneratedType(containingType))
+                return false;
+
+            if (member.IsImplicitlyDeclared || ApiConventionHelper.IsCompilerGeneratedName(member.Name))
+                return false;
+
+            return !ApiConventionHelper.HasAttribute(member, ApiConventionHelper.ExceptionToNullableEnumConventionAttribute);
+        }
+
+        private void AnalyzeMethod(SymbolAnalysisContext context)
+        {
+            var method = (IMethodSymbol)context.Symbol;
+
+            // Original test handles ordinary methods (excluding accessors) and constructors.
+            if (method.MethodKind is not (MethodKind.Ordinary or MethodKind.Constructor))
+                return;
+
+            if (!MemberPreconditions(method))
+                return;
+
+            // Original test only considers non-private methods/constructors.
+            if (method.DeclaredAccessibility == Accessibility.Private)
+                return;
+
+            // Return value (ordinary methods only; constructors have no return type to flag).
+            if (method.MethodKind == MethodKind.Ordinary && IsNullableEnum(method.ReturnType))
+            {
+                ReportSymbol(context, method);
+            }
+
+            foreach (var parameter in method.Parameters)
+            {
+                if (IsNullableEnum(parameter.Type))
+                {
+                    ReportParameter(context, parameter);
+                }
+            }
+        }
+
+        private void AnalyzeProperty(SymbolAnalysisContext context)
+        {
+            var property = (IPropertySymbol)context.Symbol;
+            if (!MemberPreconditions(property))
+                return;
+
+            // Original IsNonPrivateProperty: requires a public getter or setter.
+            if (!ApiConventionHelper.HasPublicAccessor(property))
+                return;
+
+            if (IsNullableEnum(property.Type))
+            {
+                ReportSymbol(context, property);
+            }
+        }
+
+        private void AnalyzeField(SymbolAnalysisContext context)
+        {
+            var field = (IFieldSymbol)context.Symbol;
+            if (!MemberPreconditions(field))
+                return;
+
+            // Original test flags protected (or protected internal) fields.
+            if (field.DeclaredAccessibility is not (Accessibility.Protected or Accessibility.ProtectedOrInternal))
+                return;
+
+            if (IsNullableEnum(field.Type))
+            {
+                ReportSymbol(context, field);
+            }
+        }
+
+        private static void ReportSymbol(SymbolAnalysisContext context, ISymbol symbol)
+        {
+            foreach (var location in symbol.Locations)
+            {
+                if (location.IsInSource)
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(
+                        Descriptors.LuceneDev1013_NullableEnum, location, symbol.Name));
+                }
+            }
+        }
+
+        private static void ReportParameter(SymbolAnalysisContext context, IParameterSymbol parameter)
+        {
+            foreach (var location in parameter.Locations)
+            {
+                if (location.IsInSource)
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(
+                        Descriptors.LuceneDev1013_NullableEnum, location, parameter.Name));
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs
new file mode 100644
index 0000000..aa96e9e
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForMembersAcceptingOrReturningIEnumerable: reports members
+    /// (methods, constructors, properties) that accept or return <c>IEnumerable&lt;T&gt;</c>. This
+    /// rule was a one-time porting aid and is disabled by default.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer : MembersUsingTypeAnalyzerBase
+    {
+        public LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer()
+            // Original test passes publiclyVisibleOnly: false (all members are scanned).
+            : base(Descriptors.LuceneDev1014_MemberAcceptsOrReturnsIEnumerable, publiclyVisibleOnly: false)
+        {
+        }
+
+        protected override bool IsTargetType(ITypeSymbol type) =>
+            type is INamedTypeSymbol named
+            && named.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T;
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer.cs
new file mode 100644
index 0000000..56b5b0c
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer.cs
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForMembersAcceptingOrReturningListOrDictionary: reports
+    /// non-private members (methods, constructors, properties) that accept or return
+    /// <c>List&lt;T&gt;</c> or <c>Dictionary&lt;K, V&gt;</c>; these should be exposed as
+    /// <c>IList&lt;T&gt;</c> and <c>IDictionary&lt;K, V&gt;</c> respectively.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer : MembersUsingTypeAnalyzerBase
+    {
+        private const string ListMetadataName = "System.Collections.Generic.List`1";
+        private const string DictionaryMetadataName = "System.Collections.Generic.Dictionary`2";
+
+        public LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer()
+            // Original test passes publiclyVisibleOnly: true (non-private members only).
+            : base(Descriptors.LuceneDev1015_MemberAcceptsOrReturnsListOrDictionary, publiclyVisibleOnly: true)
+        {
+        }
+
+        protected override bool IsTargetType(ITypeSymbol type)
+        {
+            if (type is not INamedTypeSymbol named)
+                return false;
+
+            // Compare the unbound generic definition by its full metadata name, which is stable
+            // across type arguments (e.g. "System.Collections.Generic.List`1").
+            var metadataName = named.OriginalDefinition.ContainingNamespace?.ToDisplayString() + "." + named.OriginalDefinition.MetadataName;
+            return metadataName == ListMetadataName || metadataName == DictionaryMetadataName;
+        }
+
+        protected override string DescribeType(ITypeSymbol type) =>
+            type.OriginalDefinition.Name; // "List" or "Dictionary"
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/MembersUsingTypeAnalyzerBase.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/MembersUsingTypeAnalyzerBase.cs
new file mode 100644
index 0000000..4350183
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/MembersUsingTypeAnalyzerBase.cs
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx
+{
+    /// <summary>
+    /// Base class for the analyzers that mirror TestApiConsistency.GetMembersAcceptingOrReturningType:
+    /// they report ordinary methods (return value and parameters), constructors (parameters), and
+    /// properties whose type is one of a set of concrete generic types. Derived classes specify the
+    /// metadata names of the target open generic types and whether only non-private members are
+    /// considered.
+    /// </summary>
+    public abstract class MembersUsingTypeAnalyzerBase : DiagnosticAnalyzer
+    {
+        private readonly DiagnosticDescriptor descriptor;
+        private readonly bool publiclyVisibleOnly;
+
+        protected MembersUsingTypeAnalyzerBase(DiagnosticDescriptor descriptor, bool publiclyVisibleOnly)
+        {
+            this.descriptor = descriptor;
+            this.publiclyVisibleOnly = publiclyVisibleOnly;
+        }
+
+        public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(descriptor);
+
+        public sealed override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
+            context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
+        }
+
+        /// <summary>
+        /// Returns <c>true</c> when the type matches one of the concrete generic types this rule
+        /// targets (compared by its unbound generic definition).
+        /// </summary>
+        protected abstract bool IsTargetType(ITypeSymbol type);
+
+        /// <summary>The display name of the matched type, used in the diagnostic message.</summary>
+        protected virtual string DescribeType(ITypeSymbol type) => type.OriginalDefinition.Name;
+
+        private bool MemberPreconditions(ISymbol member)
+        {
+            var containingType = member.ContainingType;
+            if (containingType is null || ApiConventionHelper.IsGeneratedType(containingType))
+                return false;
+
+            return !member.IsImplicitlyDeclared && !ApiConventionHelper.IsCompilerGeneratedName(member.Name);
+        }
+
+        private void AnalyzeMethod(SymbolAnalysisContext context)
+        {
+            var method = (IMethodSymbol)context.Symbol;
+
+            if (method.MethodKind is not (MethodKind.Ordinary or MethodKind.Constructor))
+                return;
+
+            if (!MemberPreconditions(method))
+                return;
+
+            if (publiclyVisibleOnly && method.DeclaredAccessibility == Accessibility.Private)
+                return;
+
+            // Constructors have no meaningful return type to flag; only ordinary methods do.
+            if (method.MethodKind == MethodKind.Ordinary && IsTargetType(method.ReturnType))
+            {
+                Report(context, method, method.ReturnType);
+            }
+
+            foreach (var parameter in method.Parameters)
+            {
+                if (IsTargetType(parameter.Type))
+                {
+                    ReportParameter(context, parameter, parameter.Type);
+                }
+            }
+        }
+
+        private void AnalyzeProperty(SymbolAnalysisContext context)
+        {
+            var property = (IPropertySymbol)context.Symbol;
+            if (!MemberPreconditions(property))
+                return;
+
+            if (publiclyVisibleOnly && !ApiConventionHelper.HasPublicAccessor(property))
+                return;
+
+            if (IsTargetType(property.Type))
+            {
+                Report(context, property, property.Type);
+            }
+        }
+
+        private void Report(SymbolAnalysisContext context, ISymbol member, ITypeSymbol matchedType)
+        {
+            foreach (var location in member.Locations)
+            {
+                if (location.IsInSource)
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(
+                        descriptor, location, member.Name, DescribeType(matchedType)));
+                }
+            }
+        }
+
+        private void ReportParameter(SymbolAnalysisContext context, IParameterSymbol parameter, ITypeSymbol matchedType)
+        {
+            foreach (var location in parameter.Locations)
+            {
+                if (location.IsInSource)
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(
+                        descriptor, location, parameter.Name, DescribeType(matchedType)));
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7000_PrivateFieldNameAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7000_PrivateFieldNameAnalyzer.cs
new file mode 100644
index 0000000..08cc254
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7000_PrivateFieldNameAnalyzer.cs
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestPrivateFieldNames: private or internal (assembly) fields of
+    /// classes must be camelCase (optionally with a leading underscore) or an UPPER_CASE constant.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7000_PrivateFieldNameAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev7000_PrivateFieldName);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field);
+        }
+
+        private static void AnalyzeField(SymbolAnalysisContext context)
+        {
+            var field = (IFieldSymbol)context.Symbol;
+
+            // Original test: only classes.
+            if (field.ContainingType?.TypeKind != TypeKind.Class)
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(field.ContainingType))
+                return;
+
+            // Ignore compiler-generated backing fields (names starting with '<').
+            if (field.IsImplicitlyDeclared || ApiConventionHelper.IsCompilerGeneratedName(field.Name))
+                return;
+
+            // Original test condition: field.IsPrivate || field.IsAssembly (internal).
+            if (field.DeclaredAccessibility is not (Accessibility.Private or Accessibility.Internal))
+                return;
+
+            if (!NamingHelper.PrivateFieldName.IsMatch(field.Name))
+            {
+                foreach (var location in field.Locations)
+                {
+                    if (location.IsInSource)
+                    {
+                        context.ReportDiagnostic(Diagnostic.Create(
+                            Descriptors.LuceneDev7000_PrivateFieldName, location, field.Name));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7001_ProtectedFieldNameAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7001_ProtectedFieldNameAnalyzer.cs
new file mode 100644
index 0000000..4ecd4cf
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7001_ProtectedFieldNameAnalyzer.cs
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestProtectedFieldNames: protected (or protected internal)
+    /// instance fields of classes must use the 'm_' prefix followed by camelCase, or be an
+    /// UPPER_CASE constant. Fields in the Lucene.Net.Support namespace and the AssemblyKeys
+    /// type are excluded, mirroring the original test.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7001_ProtectedFieldNameAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev7001_ProtectedFieldName);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field);
+        }
+
+        private static void AnalyzeField(SymbolAnalysisContext context)
+        {
+            var field = (IFieldSymbol)context.Symbol;
+            var containingType = field.ContainingType;
+
+            if (containingType?.TypeKind != TypeKind.Class)
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(containingType))
+                return;
+
+            if (ApiConventionHelper.IsInSupportNamespace(containingType))
+                return;
+
+            if (containingType.Name == "AssemblyKeys")
+                return;
+
+            // Original test binds Instance fields only (static protected fields are not scanned).
+            if (field.IsStatic)
+                return;
+
+            if (field.IsImplicitlyDeclared || ApiConventionHelper.IsCompilerGeneratedName(field.Name))
+                return;
+
+            // Original test condition: field.IsFamily || field.IsFamilyOrAssembly.
+            if (field.DeclaredAccessibility is not (Accessibility.Protected or Accessibility.ProtectedOrInternal))
+                return;
+
+            if (!NamingHelper.ProtectedFieldName.IsMatch(field.Name))
+            {
+                foreach (var location in field.Locations)
+                {
+                    if (location.IsInSource)
+                    {
+                        context.ReportDiagnostic(Diagnostic.Create(
+                            Descriptors.LuceneDev7001_ProtectedFieldName, location, field.Name));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7002_MethodParameterNameAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7002_MethodParameterNameAnalyzer.cs
new file mode 100644
index 0000000..d43bf30
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7002_MethodParameterNameAnalyzer.cs
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestMethodParameterNames: parameters of methods declared on
+    /// classes must be camelCase and not begin or end with an underscore. All accessibilities
+    /// are scanned, matching the original test.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7002_MethodParameterNameAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev7002_MethodParameterName);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
+        }
+
+        private static void AnalyzeMethod(SymbolAnalysisContext context)
+        {
+            var method = (IMethodSymbol)context.Symbol;
+            var containingType = method.ContainingType;
+
+            if (containingType?.TypeKind != TypeKind.Class)
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(containingType))
+                return;
+
+            // Original test scans c.GetMethods(...), skipping '<'-prefixed (compiler-generated)
+            // names. Reflection's GetMethods() also returns property/event accessors and operator
+            // methods; we intentionally restrict to ordinary methods. Accessor parameters are
+            // always compiler-named (e.g. 'value') and never violate, and operators/conversions
+            // do not exist in the ported Java API surface this rule targets.
+            if (method.MethodKind != MethodKind.Ordinary)
+                return;
+
+            if (method.IsImplicitlyDeclared || ApiConventionHelper.IsCompilerGeneratedName(method.Name))
+                return;
+
+            foreach (var parameter in method.Parameters)
+            {
+                if (string.IsNullOrEmpty(parameter.Name))
+                    continue;
+
+                if (!NamingHelper.MethodParameterName.IsMatch(parameter.Name))
+                {
+                    foreach (var location in parameter.Locations)
+                    {
+                        if (location.IsInSource)
+                        {
+                            context.ReportDiagnostic(Diagnostic.Create(
+                                Descriptors.LuceneDev7002_MethodParameterName, location, parameter.Name));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7003_InterfaceNameAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7003_InterfaceNameAnalyzer.cs
new file mode 100644
index 0000000..14ce35e
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7003_InterfaceNameAnalyzer.cs
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestInterfaceNames: interface names must begin with a capital
+    /// 'I' followed by another capital letter (with an optional generic-arity suffix).
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7003_InterfaceNameAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev7003_InterfaceName);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeType, SymbolKind.NamedType);
+        }
+
+        private static void AnalyzeType(SymbolAnalysisContext context)
+        {
+            var type = (INamedTypeSymbol)context.Symbol;
+
+            if (type.TypeKind != TypeKind.Interface)
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(type))
+                return;
+
+            // The original tests match on the metadata name, which carries the generic arity
+            // suffix (e.g. "IComparer`1"); MetadataName preserves that.
+            if (!NamingHelper.InterfaceName.IsMatch(type.MetadataName))
+            {
+                foreach (var location in type.Locations)
+                {
+                    if (location.IsInSource)
+                    {
+                        context.ReportDiagnostic(Diagnostic.Create(
+                            Descriptors.LuceneDev7003_InterfaceName, location, type.Name));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7004_ClassNameAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7004_ClassNameAnalyzer.cs
new file mode 100644
index 0000000..e4a3eaa
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7004_ClassNameAnalyzer.cs
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestClassNames: class names must be PascalCase and must not
+    /// follow the interface naming convention (capital 'I' followed by another capital letter).
+    /// Classes decorated with [ExceptionToClassNameConvention] are excluded.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7004_ClassNameAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev7004_ClassName);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeType, SymbolKind.NamedType);
+        }
+
+        private static void AnalyzeType(SymbolAnalysisContext context)
+        {
+            var type = (INamedTypeSymbol)context.Symbol;
+
+            // The original test scanned reflection's t.IsClass, which is true for both classes and
+            // delegates (delegates are CLR classes). Delegate names follow the same convention.
+            if (type.TypeKind is not (TypeKind.Class or TypeKind.Delegate))
+                return;
+
+            if (ApiConventionHelper.IsGeneratedType(type))
+                return;
+
+            // Ignore compiler-produced classes (e.g. anonymous types) whose names start with '<'.
+            if (ApiConventionHelper.IsCompilerGeneratedName(type.MetadataName))
+                return;
+
+            if (ApiConventionHelper.HasAttribute(type, ApiConventionHelper.ExceptionToClassNameConventionAttribute))
+                return;
+
+            // Original test: invalid when it is not a valid class name OR it looks like an interface name.
+            if (!NamingHelper.ClassName.IsMatch(type.MetadataName) || NamingHelper.InterfaceName.IsMatch(type.MetadataName))
+            {
+                foreach (var location in type.Locations)
+                {
+                    if (location.IsInSource)
+                    {
+                        context.ReportDiagnostic(Diagnostic.Create(
+                            Descriptors.LuceneDev7004_ClassName, location, type.Name));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7005_MemberContainsComparerAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7005_MemberContainsComparerAnalyzer.cs
new file mode 100644
index 0000000..6fb4600
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7005_MemberContainsComparerAnalyzer.cs
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForPublicMembersContainingComparer: reports protected fields,
+    /// externally visible types, and members (ordinary methods, properties and events) whose name
+    /// contains the Java term "Comparator". In .NET these should use "Comparer".
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7005_MemberContainsComparerAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev7005_MemberContainsComparer);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeType, SymbolKind.NamedType);
+            context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field);
+            context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
+            context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
+            context.RegisterSymbolAction(AnalyzeEvent, SymbolKind.Event);
+        }
+
+        private static bool Matches(string name) => NamingHelper.ContainsComparer.IsMatch(name);
+
+        private void AnalyzeType(SymbolAnalysisContext context)
+        {
+            var type = (INamedTypeSymbol)context.Symbol;
+
+            if (ApiConventionHelper.IsGeneratedType(type))
+                return;
+
+            // Original test: only externally visible types are flagged (t.IsVisible).
+            if (Matches(type.MetadataName) && IsVisible(type))
+                Report(context, type);
+        }
+
+        private void AnalyzeField(SymbolAnalysisContext context)
+        {
+            var field = (IFieldSymbol)context.Symbol;
+            var containingType = field.ContainingType;
+
+            if (containingType?.TypeKind != TypeKind.Class || ApiConventionHelper.IsGeneratedType(containingType))
+                return;
+
+            if (field.IsStatic || field.IsImplicitlyDeclared || ApiConventionHelper.IsCompilerGeneratedName(field.Name))
+                return;
+
+            // Original test scans protected (or protected internal) instance fields.
+            if (field.DeclaredAccessibility is not (Accessibility.Protected or Accessibility.ProtectedOrInternal))
+                return;
+
+            if (Matches(field.Name))
+                Report(context, field);
+        }
+
+        private void AnalyzeMethod(SymbolAnalysisContext context)
+        {
+            var method = (IMethodSymbol)context.Symbol;
+            // The original test also sees explicit interface implementations, which reflection
+            // reports under their qualified name (e.g. "IComparator.Compare").
+            if (method.MethodKind is not (MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation))
+                return;
+
+            if (ShouldExamineMember(method) && Matches(method.Name))
+                Report(context, method);
+        }
+
+        private void AnalyzeProperty(SymbolAnalysisContext context)
+        {
+            var property = (IPropertySymbol)context.Symbol;
+            if (ShouldExamineMember(property) && Matches(property.Name))
+                Report(context, property);
+        }
+
+        private void AnalyzeEvent(SymbolAnalysisContext context)
+        {
+            var @event = (IEventSymbol)context.Symbol;
+            if (ShouldExamineMember(@event) && Matches(@event.Name))
+                Report(context, @event);
+        }
+
+        private static bool ShouldExamineMember(ISymbol member)
+        {
+            var containingType = member.ContainingType;
+            if (containingType is null || ApiConventionHelper.IsGeneratedType(containingType))
+                return false;
+
+            return !member.IsImplicitlyDeclared && !ApiConventionHelper.IsCompilerGeneratedName(member.Name);
+        }
+
+        private static bool IsVisible(INamedTypeSymbol type)
+        {
+            for (var current = type; current is not null; current = current.ContainingType)
+            {
+                if (current.DeclaredAccessibility != Accessibility.Public)
+                    return false;
+            }
+
+            return true;
+        }
+
+        private void Report(SymbolAnalysisContext context, ISymbol symbol)
+        {
+            foreach (var location in symbol.Locations)
+            {
+                if (location.IsInSource)
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(
+                        Descriptors.LuceneDev7005_MemberContainsComparer, location, symbol.Name));
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7006_MemberNamedSizeAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7006_MemberNamedSizeAnalyzer.cs
new file mode 100644
index 0000000..779d03e
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7006_MemberNamedSizeAnalyzer.cs
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForPublicMembersNamedSize: reports parameterless methods and
+    /// properties named "Size" (case-insensitive). In .NET these should be "Count" or "Length".
+    /// Events are not considered, matching the original test.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7006_MemberNamedSizeAnalyzer : MemberNamePredicateAnalyzerBase
+    {
+        public LuceneDev7006_MemberNamedSizeAnalyzer()
+            : base(Descriptors.LuceneDev7006_MemberNamedSize)
+        {
+        }
+
+        protected override bool IsViolation(string name) =>
+            "Size".Equals(name, StringComparison.OrdinalIgnoreCase);
+
+        // Original test only flags parameterless methods named Size.
+        protected override bool IncludeMethod(IMethodSymbol method) => method.Parameters.Length == 0;
+
+        // Original test does not consider events.
+        protected override bool IncludeEvents => false;
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7007_MemberContainsNonNetNumericAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7007_MemberContainsNonNetNumericAnalyzer.cs
new file mode 100644
index 0000000..d67c2d8
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7007_MemberContainsNonNetNumericAnalyzer.cs
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForPublicMembersContainingNonNetNumeric: reports ordinary
+    /// methods, properties and events whose name contains a Java-style numeric term ('Int' not
+    /// followed by 16/32/64, 'Long', 'Short' or 'Float'). Members decorated with
+    /// [ExceptionToNetNumericConvention] are excluded.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7007_MemberContainsNonNetNumericAnalyzer : MemberNamePredicateAnalyzerBase
+    {
+        public LuceneDev7007_MemberContainsNonNetNumericAnalyzer()
+            : base(Descriptors.LuceneDev7007_MemberContainsNonNetNumeric)
+        {
+        }
+
+        protected override bool IsViolation(string name) =>
+            NamingHelper.ContainsNonNetNumeric.IsMatch(name);
+
+        protected override bool ShouldSkipMember(ISymbol member) =>
+            ApiConventionHelper.HasAttribute(member, ApiConventionHelper.ExceptionToNetNumericConventionAttribute);
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7008_TypeContainsNonNetNumericAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7008_TypeContainsNonNetNumericAnalyzer.cs
new file mode 100644
index 0000000..207e661
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/LuceneDev7008_TypeContainsNonNetNumericAnalyzer.cs
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Ports TestApiConsistency.TestForTypesContainingNonNetNumeric: reports types whose name
+    /// contains a Java-style numeric term ('Int' not followed by 16/32/64, 'Long', 'Short' or
+    /// 'Float'). Types decorated with [ExceptionToNetNumericConvention] are excluded.
+    /// </summary>
+    [DiagnosticAnalyzer(LanguageNames.CSharp)]
+    public sealed class LuceneDev7008_TypeContainsNonNetNumericAnalyzer : DiagnosticAnalyzer
+    {
+        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(Descriptors.LuceneDev7008_TypeContainsNonNetNumeric);
+
+        public override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeType, SymbolKind.NamedType);
+        }
+
+        private static void AnalyzeType(SymbolAnalysisContext context)
+        {
+            var type = (INamedTypeSymbol)context.Symbol;
+
+            if (ApiConventionHelper.IsGeneratedType(type))
+                return;
+
+            if (ApiConventionHelper.HasAttribute(type, ApiConventionHelper.ExceptionToNetNumericConventionAttribute))
+                return;
+
+            if (NamingHelper.ContainsNonNetNumeric.IsMatch(type.MetadataName))
+            {
+                foreach (var location in type.Locations)
+                {
+                    if (location.IsInSource)
+                    {
+                        context.ReportDiagnostic(Diagnostic.Create(
+                            Descriptors.LuceneDev7008_TypeContainsNonNetNumeric, location, type.Name));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/MemberNamePredicateAnalyzerBase.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/MemberNamePredicateAnalyzerBase.cs
new file mode 100644
index 0000000..3e029fa
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev7xxx/MemberNamePredicateAnalyzerBase.cs
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Collections.Immutable;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx
+{
+    /// <summary>
+    /// Base class for the member-name analyzers that mirror the LUCENENET TestApiConsistency
+    /// rules which scan all members of all types (regardless of accessibility) and report when a
+    /// member's name matches a predicate. Like the original tests, these examine ordinary methods
+    /// (excluding property/event accessors), properties and events. Derived classes provide the
+    /// descriptor and the name predicate, and may override member-kind handling.
+    /// </summary>
+    public abstract class MemberNamePredicateAnalyzerBase : DiagnosticAnalyzer
+    {
+        private readonly DiagnosticDescriptor descriptor;
+
+        protected MemberNamePredicateAnalyzerBase(DiagnosticDescriptor descriptor)
+        {
+            this.descriptor = descriptor;
+        }
+
+        public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
+            ImmutableArray.Create(descriptor);
+
+        public sealed override void Initialize(AnalysisContext context)
+        {
+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+            context.EnableConcurrentExecution();
+            context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
+            context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
+            if (IncludeEvents)
+                context.RegisterSymbolAction(AnalyzeEvent, SymbolKind.Event);
+        }
+
+        /// <summary>Returns <c>true</c> when the given name violates this rule.</summary>
+        protected abstract bool IsViolation(string name);
+
+        /// <summary>
+        /// Allows derived classes to further restrict which ordinary methods are considered
+        /// (e.g. only parameterless methods). Defaults to all ordinary methods.
+        /// </summary>
+        protected virtual bool IncludeMethod(IMethodSymbol method) => true;
+
+        /// <summary>
+        /// Whether event members participate in this rule. Defaults to <c>true</c>; the
+        /// 'Size' rule excludes events to match the original test.
+        /// </summary>
+        protected virtual bool IncludeEvents => true;
+
+        /// <summary>
+        /// Allows derived classes to skip a member entirely (e.g. when it carries a
+        /// LUCENENET suppression attribute). Defaults to never skipping.
+        /// </summary>
+        protected virtual bool ShouldSkipMember(ISymbol member) => false;
+
+        private void AnalyzeMethod(SymbolAnalysisContext context)
+        {
+            var method = (IMethodSymbol)context.Symbol;
+
+            // Original tests examine ordinary methods, excluding get_/set_ accessors.
+            if (method.MethodKind != MethodKind.Ordinary)
+                return;
+
+            if (!ShouldExamine(method, method.ContainingType))
+                return;
+
+            if (!IncludeMethod(method))
+                return;
+
+            ReportIfViolation(context, method);
+        }
+
+        private void AnalyzeProperty(SymbolAnalysisContext context)
+        {
+            var property = (IPropertySymbol)context.Symbol;
+            if (!ShouldExamine(property, property.ContainingType))
+                return;
+
+            ReportIfViolation(context, property);
+        }
+
+        private void AnalyzeEvent(SymbolAnalysisContext context)
+        {
+            var @event = (IEventSymbol)context.Symbol;
+            if (!ShouldExamine(@event, @event.ContainingType))
+                return;
+
+            ReportIfViolation(context, @event);
+        }
+
+        private static bool ShouldExamine(ISymbol member, INamedTypeSymbol? containingType)
+        {
+            if (containingType is null)
+                return false;
+
+            if (ApiConventionHelper.IsGeneratedType(containingType))
+                return false;
+
+            if (member.IsImplicitlyDeclared || ApiConventionHelper.IsCompilerGeneratedName(member.Name))
+                return false;
+
+            return true;
+        }
+
+        private void ReportIfViolation(SymbolAnalysisContext context, ISymbol member)
+        {
+            if (ShouldSkipMember(member))
+                return;
+
+            if (!IsViolation(member.Name))
+                return;
+
+            foreach (var location in member.Locations)
+            {
+                if (location.IsInSource)
+                {
+                    context.ReportDiagnostic(Diagnostic.Create(descriptor, location, member.Name));
+                }
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx b/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx
index 2bf6cb7..e02157f 100644
--- a/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx
+++ b/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx
@@ -439,4 +439,180 @@ under the License.
     <value>'{0}' is referenced by this StackTraceHelper.DoesStackTraceContainMethod call and should be marked with [MethodImpl(MethodImplOptions.NoInlining)] to prevent the JIT from inlining it out of the stack trace.</value>
   </data>
 
+  <!-- 1009: Public fields -->
+  <data name="LuceneDev1009_AnalyzerTitle" xml:space="preserve">
+    <value>Fields should not be public</value>
+  </data>
+  <data name="LuceneDev1009_AnalyzerDescription" xml:space="preserve">
+    <value>Public fields break encapsulation and cannot be changed to properties later without breaking binary compatibility. Use a public property instead.</value>
+  </data>
+  <data name="LuceneDev1009_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Field '{0}' should not be public; consider using a public property instead.</value>
+  </data>
+
+  <!-- 1010: Property with a setter but no getter -->
+  <data name="LuceneDev1010_AnalyzerTitle" xml:space="preserve">
+    <value>Properties should not have a setter without a getter</value>
+  </data>
+  <data name="LuceneDev1010_AnalyzerDescription" xml:space="preserve">
+    <value>A property with a setter but no getter is write-only, which is unintuitive. Provide a getter, or expose a SetXxx method instead.</value>
+  </data>
+  <data name="LuceneDev1010_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Property '{0}' has a setter but no getter; provide a getter or use a SetXxx method instead.</value>
+  </data>
+
+  <!-- 1011: Property returns an array -->
+  <data name="LuceneDev1011_AnalyzerTitle" xml:space="preserve">
+    <value>Properties should not return arrays</value>
+  </data>
+  <data name="LuceneDev1011_AnalyzerDescription" xml:space="preserve">
+    <value>A property that returns an array allows callers to mutate the array contents, and may allocate a new array on each access. Return a read-only collection, or expose a method that makes the allocation explicit.</value>
+  </data>
+  <data name="LuceneDev1011_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Property '{0}' returns an array; consider returning a read-only collection or exposing a method instead.</value>
+  </data>
+
+  <!-- 1012: Method returns a writable array -->
+  <data name="LuceneDev1012_AnalyzerTitle" xml:space="preserve">
+    <value>Methods should not return writable arrays</value>
+  </data>
+  <data name="LuceneDev1012_AnalyzerDescription" xml:space="preserve">
+    <value>A method that returns a writable array allows callers to mutate internal state. Return a read-only collection, or document and intentionally return a copy.</value>
+  </data>
+  <data name="LuceneDev1012_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Method '{0}' returns a writable array; consider returning a read-only collection instead.</value>
+  </data>
+
+  <!-- 1013: Nullable enum member -->
+  <data name="LuceneDev1013_AnalyzerTitle" xml:space="preserve">
+    <value>Public members should not be nullable enums</value>
+  </data>
+  <data name="LuceneDev1013_AnalyzerDescription" xml:space="preserve">
+    <value>A nullable enum public member is usually an artifact of the Java port. Prefer a non-nullable enum with an explicit 'None'/default value, or reconsider the API design.</value>
+  </data>
+  <data name="LuceneDev1013_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Member '{0}' is a nullable enum; consider using a non-nullable enum with an explicit default value instead.</value>
+  </data>
+
+  <!-- 1014: Member accepts or returns IEnumerable (disabled by default; one-time port aid) -->
+  <data name="LuceneDev1014_AnalyzerTitle" xml:space="preserve">
+    <value>Members should not accept or return IEnumerable&lt;T&gt;</value>
+  </data>
+  <data name="LuceneDev1014_AnalyzerDescription" xml:space="preserve">
+    <value>This rule identifies members that were changed from ICollection, IList or ISet to IEnumerable&lt;T&gt; during the Java port, which may have changed the API contract. It is disabled by default and intended as a one-time review aid.</value>
+  </data>
+  <data name="LuceneDev1014_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Member '{0}' accepts or returns IEnumerable&lt;T&gt;; verify whether ICollection, IList or ISet is more appropriate.</value>
+  </data>
+
+  <!-- 1015: Member accepts or returns List&lt;T&gt; or Dictionary&lt;K,V&gt; -->
+  <data name="LuceneDev1015_AnalyzerTitle" xml:space="preserve">
+    <value>Members should not accept or return List&lt;T&gt; or Dictionary&lt;K, V&gt;</value>
+  </data>
+  <data name="LuceneDev1015_AnalyzerDescription" xml:space="preserve">
+    <value>Exposing concrete collection types like List&lt;T&gt; or Dictionary&lt;K, V&gt; in public APIs leaks implementation details. Prefer interfaces such as IList&lt;T&gt;, IReadOnlyList&lt;T&gt;, IDictionary&lt;K, V&gt; or IReadOnlyDictionary&lt;K, V&gt;.</value>
+  </data>
+  <data name="LuceneDev1015_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Member '{0}' accepts or returns '{1}'; prefer an interface type such as IList&lt;T&gt; or IDictionary&lt;K, V&gt; instead.</value>
+  </data>
+
+  <!-- 7000: Private field name -->
+  <data name="LuceneDev7000_AnalyzerTitle" xml:space="preserve">
+    <value>Private fields should follow the naming convention</value>
+  </data>
+  <data name="LuceneDev7000_AnalyzerDescription" xml:space="preserve">
+    <value>Private fields should be named using camelCase, optionally prefixed with an underscore (e.g. 'field' or '_field'), or be UPPER_CASE for constants.</value>
+  </data>
+  <data name="LuceneDev7000_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Private field '{0}' does not follow the naming convention; use camelCase, optionally with a leading underscore.</value>
+  </data>
+
+  <!-- 7001: Protected field name -->
+  <data name="LuceneDev7001_AnalyzerTitle" xml:space="preserve">
+    <value>Protected fields should follow the naming convention</value>
+  </data>
+  <data name="LuceneDev7001_AnalyzerDescription" xml:space="preserve">
+    <value>Protected (and protected internal) fields should be named using the 'm_' prefix followed by camelCase (e.g. 'm_field'), or be UPPER_CASE for constants.</value>
+  </data>
+  <data name="LuceneDev7001_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Protected field '{0}' does not follow the naming convention; use the 'm_' prefix followed by camelCase.</value>
+  </data>
+
+  <!-- 7002: Method parameter name -->
+  <data name="LuceneDev7002_AnalyzerTitle" xml:space="preserve">
+    <value>Method parameters should follow the naming convention</value>
+  </data>
+  <data name="LuceneDev7002_AnalyzerDescription" xml:space="preserve">
+    <value>Method parameters should be named using camelCase, starting with a lowercase letter.</value>
+  </data>
+  <data name="LuceneDev7002_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Method parameter '{0}' does not follow the naming convention; use camelCase.</value>
+  </data>
+
+  <!-- 7003: Interface name -->
+  <data name="LuceneDev7003_AnalyzerTitle" xml:space="preserve">
+    <value>Interface names should begin with 'I'</value>
+  </data>
+  <data name="LuceneDev7003_AnalyzerDescription" xml:space="preserve">
+    <value>Interface names should begin with a capital 'I' followed by PascalCase (e.g. 'IComparer').</value>
+  </data>
+  <data name="LuceneDev7003_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Interface '{0}' does not follow the naming convention; interface names should begin with 'I'.</value>
+  </data>
+
+  <!-- 7004: Class name -->
+  <data name="LuceneDev7004_AnalyzerTitle" xml:space="preserve">
+    <value>Class names should follow the naming convention</value>
+  </data>
+  <data name="LuceneDev7004_AnalyzerDescription" xml:space="preserve">
+    <value>Class names should use PascalCase, starting with an uppercase letter.</value>
+  </data>
+  <data name="LuceneDev7004_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Class '{0}' does not follow the naming convention; use PascalCase.</value>
+  </data>
+
+  <!-- 7005: Member contains 'Comparator' -->
+  <data name="LuceneDev7005_AnalyzerTitle" xml:space="preserve">
+    <value>Public members should not contain the word 'Comparator'</value>
+  </data>
+  <data name="LuceneDev7005_AnalyzerDescription" xml:space="preserve">
+    <value>'Comparator' is a Java term. In .NET the equivalent is 'Comparer'. Rename the member to use 'Comparer'.</value>
+  </data>
+  <data name="LuceneDev7005_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Member '{0}' contains the word 'Comparator'; use the .NET term 'Comparer' instead.</value>
+  </data>
+
+  <!-- 7006: Member named 'Size' -->
+  <data name="LuceneDev7006_AnalyzerTitle" xml:space="preserve">
+    <value>Public members should not be named 'Size'</value>
+  </data>
+  <data name="LuceneDev7006_AnalyzerDescription" xml:space="preserve">
+    <value>'Size' is a Java naming convention. In .NET, prefer 'Count' for the number of elements or 'Length' for arrays and strings.</value>
+  </data>
+  <data name="LuceneDev7006_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Member '{0}' is named 'Size'; use 'Count' or 'Length' instead, per .NET conventions.</value>
+  </data>
+
+  <!-- 7007: Member contains non-.NET numeric term -->
+  <data name="LuceneDev7007_AnalyzerTitle" xml:space="preserve">
+    <value>Public members should not contain non-.NET numeric type names</value>
+  </data>
+  <data name="LuceneDev7007_AnalyzerDescription" xml:space="preserve">
+    <value>Member names should use .NET numeric type names. Java-style numeric terms such as 'Int', 'Long', 'Short' and 'Float' should be replaced with 'Int32', 'Int64', 'Int16' and 'Single' respectively.</value>
+  </data>
+  <data name="LuceneDev7007_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Member '{0}' contains a non-.NET numeric term; use .NET numeric type names such as Int32, Int64, Int16 or Single.</value>
+  </data>
+
+  <!-- 7008: Type contains non-.NET numeric term -->
+  <data name="LuceneDev7008_AnalyzerTitle" xml:space="preserve">
+    <value>Type names should not contain non-.NET numeric type names</value>
+  </data>
+  <data name="LuceneDev7008_AnalyzerDescription" xml:space="preserve">
+    <value>Type names should use .NET numeric type names. Java-style numeric terms such as 'Int', 'Long', 'Short' and 'Float' should be replaced with 'Int32', 'Int64', 'Int16' and 'Single' respectively.</value>
+  </data>
+  <data name="LuceneDev7008_AnalyzerMessageFormat" xml:space="preserve">
+    <value>Type '{0}' contains a non-.NET numeric term; use .NET numeric type names such as Int32, Int64, Int16 or Single.</value>
+  </data>
+
 </root>
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/ApiConventionHelper.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/ApiConventionHelper.cs
new file mode 100644
index 0000000..cbc1126
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/ApiConventionHelper.cs
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Linq;
+using Microsoft.CodeAnalysis;
+
+namespace Lucene.Net.CodeAnalysis.Dev.Utility
+{
+    /// <summary>
+    /// Shared helpers for the analyzers ported from the LUCENENET TestApiConsistency
+    /// (ApiScanTestBase) rules. These mirror behaviors of the original reflection-based
+    /// tests, including the LUCENENET-specific suppression attributes.
+    /// </summary>
+    public static class ApiConventionHelper
+    {
+        // The LUCENENET attributes that suppressed individual TestApiConsistency findings.
+        // They are matched by simple name so existing lucenenet suppressions keep working
+        // without this analyzer assembly having to reference the attribute types.
+        public const string WritableArrayAttribute = "WritableArrayAttribute";
+        public const string ExceptionToClassNameConventionAttribute = "ExceptionToClassNameConventionAttribute";
+        public const string ExceptionToNetNumericConventionAttribute = "ExceptionToNetNumericConventionAttribute";
+        public const string ExceptionToNullableEnumConventionAttribute = "ExceptionToNullableEnumConventionAttribute";
+
+        /// <summary>
+        /// Returns <c>true</c> when <paramref name="symbol"/> is decorated with an attribute
+        /// whose class is named <paramref name="attributeSimpleName"/> (e.g. "WritableArrayAttribute").
+        /// </summary>
+        public static bool HasAttribute(ISymbol symbol, string attributeSimpleName) =>
+            symbol.GetAttributes().Any(a => a.AttributeClass?.Name == attributeSimpleName);
+
+        /// <summary>
+        /// Mirrors the original tests' exclusion of compiler/auto-generated members, which were
+        /// identified by a name beginning with '&lt;' (e.g. backing fields and local-function methods).
+        /// </summary>
+        public static bool IsCompilerGeneratedName(string name) =>
+            name.StartsWith("<", System.StringComparison.Ordinal);
+
+        /// <summary>
+        /// The original tests ignored types decorated with [GeneratedCode] or [CompilerGenerated].
+        /// </summary>
+        public static bool IsGeneratedType(INamedTypeSymbol type) =>
+            HasAttribute(type, "GeneratedCodeAttribute") || HasAttribute(type, "CompilerGeneratedAttribute");
+
+        /// <summary>
+        /// Returns <c>true</c> when the type is in the Lucene.Net.Support namespace (or a child),
+        /// which several of the original rules skipped.
+        /// </summary>
+        public static bool IsInSupportNamespace(INamedTypeSymbol type)
+        {
+            var ns = type.ContainingNamespace?.ToDisplayString();
+            return ns is not null && ns.StartsWith("Lucene.Net.Support", System.StringComparison.Ordinal);
+        }
+
+        /// <summary>
+        /// Mirrors the original tests' IsNonPrivateProperty helper, which used the parameterless
+        /// PropertyInfo.GetGetMethod()/GetSetMethod() overloads. Those return an accessor only when
+        /// it is <c>public</c>, so a property "counts" only when it has a public getter or setter.
+        /// </summary>
+        public static bool HasPublicAccessor(IPropertySymbol property) =>
+            property.GetMethod is { DeclaredAccessibility: Accessibility.Public }
+            || property.SetMethod is { DeclaredAccessibility: Accessibility.Public };
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs
index b2ae98e..804a3ba 100644
--- a/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs
+++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs
@@ -91,5 +91,57 @@ public static partial class Descriptors
                 Design,
                 Warning
             );
+
+        public static readonly DiagnosticDescriptor LuceneDev1009_PublicFields =
+            Diagnostic(
+                "LuceneDev1009",
+                Design,
+                Warning
+            );
+
+        public static readonly DiagnosticDescriptor LuceneDev1010_PropertyWithNoGetter =
+            Diagnostic(
+                "LuceneDev1010",
+                Design,
+                Warning
+            );
+
+        public static readonly DiagnosticDescriptor LuceneDev1011_PropertyReturnsArray =
+            Diagnostic(
+                "LuceneDev1011",
+                Design,
+                Warning
+            );
+
+        public static readonly DiagnosticDescriptor LuceneDev1012_MethodReturnsWritableArray =
+            Diagnostic(
+                "LuceneDev1012",
+                Design,
+                Warning
+            );
+
+        public static readonly DiagnosticDescriptor LuceneDev1013_NullableEnum =
+            Diagnostic(
+                "LuceneDev1013",
+                Design,
+                Warning
+            );
+
+        // Disabled by default: this rule was only ever used as a one-time porting aid to identify
+        // members changed from ICollection/IList/ISet to IEnumerable during the Java port.
+        public static readonly DiagnosticDescriptor LuceneDev1014_MemberAcceptsOrReturnsIEnumerable =
+            Diagnostic(
+                "LuceneDev1014",
+                Design,
+                Warning,
+                isEnabledByDefault: false
+            );
+
+        public static readonly DiagnosticDescriptor LuceneDev1015_MemberAcceptsOrReturnsListOrDictionary =
+            Diagnostic(
+                "LuceneDev1015",
+                Design,
+                Warning
+            );
     }
 }
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev7xxx.cs
similarity index 56%
copy from src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs
copy to src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev7xxx.cs
index b2ae98e..e18a118 100644
--- a/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs
+++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev7xxx.cs
@@ -27,68 +27,68 @@ public static partial class Descriptors
     {
         // IMPORTANT: Do not make these into properties! The AnalyzerReleases release management
         // analyzers do not recognize them and will report RS2002 warnings if it cannot read the
-        // DignosticDescriptor instance through a field.
+        // DiagnosticDescriptor instance through a field.
 
-        public static readonly DiagnosticDescriptor LuceneDev1000_FloatingPointEquality =
+        public static readonly DiagnosticDescriptor LuceneDev7000_PrivateFieldName =
             Diagnostic(
-                "LuceneDev1000",
-                Design,
+                "LuceneDev7000",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1001_FloatingPointFormatting =
+        public static readonly DiagnosticDescriptor LuceneDev7001_ProtectedFieldName =
             Diagnostic(
-                "LuceneDev1001",
-                Design,
+                "LuceneDev7001",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1002_FloatingPointArithmetic =
+        public static readonly DiagnosticDescriptor LuceneDev7002_MethodParameterName =
             Diagnostic(
-                "LuceneDev1002",
-                Design,
+                "LuceneDev7002",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1003_ArrayMethodParameter =
+        public static readonly DiagnosticDescriptor LuceneDev7003_InterfaceName =
             Diagnostic(
-                "LuceneDev1003",
-                Design,
+                "LuceneDev7003",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1004_ArrayMethodReturnValue =
+        public static readonly DiagnosticDescriptor LuceneDev7004_ClassName =
             Diagnostic(
-                "LuceneDev1004",
-                Design,
+                "LuceneDev7004",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1005_LuceneNetSupportPublicTypes =
+        public static readonly DiagnosticDescriptor LuceneDev7005_MemberContainsComparer =
             Diagnostic(
-                "LuceneDev1005",
-                Design,
+                "LuceneDev7005",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1006_FloatingPointFormatting =
+        public static readonly DiagnosticDescriptor LuceneDev7006_MemberNamedSize =
             Diagnostic(
-                "LuceneDev1006",
-                Design,
+                "LuceneDev7006",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1007_GenericDictionaryIndexerValueType =
+        public static readonly DiagnosticDescriptor LuceneDev7007_MemberContainsNonNetNumeric =
             Diagnostic(
-                "LuceneDev1007",
-                Design,
+                "LuceneDev7007",
+                Naming,
                 Warning
             );
 
-        public static readonly DiagnosticDescriptor LuceneDev1008_GenericDictionaryIndexerReferenceType =
+        public static readonly DiagnosticDescriptor LuceneDev7008_TypeContainsNonNetNumeric =
             Diagnostic(
-                "LuceneDev1008",
-                Design,
+                "LuceneDev7008",
+                Naming,
                 Warning
             );
     }
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/FieldNameHelper.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/FieldNameHelper.cs
new file mode 100644
index 0000000..38f93e4
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/FieldNameHelper.cs
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Lucene.Net.CodeAnalysis.Dev.Utility
+{
+    /// <summary>
+    /// Helpers for classifying the declared accessibility of a field declaration by its
+    /// syntactic modifiers. Shared by the field-related analyzers (LuceneDev7000,
+    /// LuceneDev7001 and LuceneDev1009).
+    /// </summary>
+    public static class FieldNameHelper
+    {
+        /// <summary>
+        /// Returns <c>true</c> when the field has <c>protected</c> accessibility
+        /// (including <c>protected internal</c> and <c>private protected</c>).
+        /// </summary>
+        public static bool IsProtected(FieldDeclarationSyntax field) =>
+            field.Modifiers.Any(SyntaxKind.ProtectedKeyword);
+
+        /// <summary>
+        /// Returns <c>true</c> when the field has <c>public</c> accessibility.
+        /// </summary>
+        public static bool IsPublic(FieldDeclarationSyntax field) =>
+            field.Modifiers.Any(SyntaxKind.PublicKeyword);
+
+        /// <summary>
+        /// Returns <c>true</c> when the field is effectively private, i.e. it has no
+        /// accessibility modifier (the C# default for a field) or an explicit
+        /// <c>private</c> modifier, and is not <c>protected</c>, <c>internal</c> or
+        /// <c>public</c>.
+        /// </summary>
+        public static bool IsPrivate(FieldDeclarationSyntax field)
+        {
+            if (field.Modifiers.Any(SyntaxKind.PublicKeyword)
+                || field.Modifiers.Any(SyntaxKind.ProtectedKeyword)
+                || field.Modifiers.Any(SyntaxKind.InternalKeyword))
+            {
+                return false;
+            }
+
+            return true; // explicit 'private' or no accessibility modifier
+        }
+    }
+}
diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/NamingHelper.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/NamingHelper.cs
new file mode 100644
index 0000000..0321dbe
--- /dev/null
+++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/NamingHelper.cs
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Text.RegularExpressions;
+
+namespace Lucene.Net.CodeAnalysis.Dev.Utility
+{
+    /// <summary>
+    /// Shared naming-convention helpers ported from the LUCENENET TestApiConsistency
+    /// (ApiScanTestBase) rules. Centralizes the regular expressions so the individual
+    /// naming analyzers (LuceneDev7000-LuceneDev7008) stay small and consistent.
+    /// </summary>
+    public static class NamingHelper
+    {
+        // Private field names: camelCase, optionally with a leading underscore, OR an UPPER_CASE constant.
+        public static readonly Regex PrivateFieldName = new Regex(
+            "^_?[a-z][a-zA-Z0-9_]*$|^[A-Z0-9_]+$", RegexOptions.Compiled);
+
+        // Protected field names: 'm_' prefix followed by camelCase, OR an UPPER_CASE constant.
+        public static readonly Regex ProtectedFieldName = new Regex(
+            "^m_[a-z][a-zA-Z0-9_]*$|^[A-Z0-9_]+$", RegexOptions.Compiled);
+
+        // Method parameter names: camelCase.
+        public static readonly Regex MethodParameterName = new Regex(
+            "^[a-z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$", RegexOptions.Compiled);
+
+        // Interface names: 'I' prefix followed by PascalCase (optionally with a generic arity suffix).
+        public static readonly Regex InterfaceName = new Regex(
+            "^I[A-Z][a-zA-Z0-9_]*(?:`\\d+)?$", RegexOptions.Compiled);
+
+        // Class names: PascalCase (optionally with a generic arity suffix).
+        public static readonly Regex ClassName = new Regex(
+            "^[A-Z][a-zA-Z0-9_]*(?:`\\d+)?$", RegexOptions.Compiled);
+
+        // Names containing the Java term "comparator" (case-insensitive).
+        public static readonly Regex ContainsComparer = new Regex(
+            "[Cc]omparator", RegexOptions.Compiled);
+
+        // Names containing a Java-style numeric term (Int/Long/Short/Float) that is not a valid
+        // .NET numeric type name (Int16/Int32/Int64 etc.) or an innocuous substring (point, print,
+        // join, integer, longest, shortest, ...).
+        public static readonly Regex ContainsNonNetNumeric = new Regex(
+            "(?<![Pp]o|[Pp]r|[Jj]o)[Ii]nt(?!16|32|64|er|eg|ro)|[Ll]ong(?!est|er)|[Ss]hort(?!est|er)|[Ff]loat",
+            RegexOptions.Compiled);
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev7xxx/TestLuceneDev7001_ProtectedFieldNameCodeFixProvider.cs b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev7xxx/TestLuceneDev7001_ProtectedFieldNameCodeFixProvider.cs
new file mode 100644
index 0000000..19461f8
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev7xxx/TestLuceneDev7001_ProtectedFieldNameCodeFixProvider.cs
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev.CodeFixes;
+
+public class TestLuceneDev7001_ProtectedFieldNameCodeFixProvider
+{
+    [Test]
+    public async Task RenamesProtectedFieldToMPrefix()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                protected int count;
+
+                protected int Get() => count;
+            }
+            """;
+
+        const string fixedCode =
+            """
+            public class MyClass
+            {
+                protected int m_count;
+
+                protected int Get() => m_count;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7001_ProtectedFieldName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("count")
+            .WithLocation(3, 19);
+
+        var test = new InjectableCodeFixTest(
+            () => new LuceneDev7001_ProtectedFieldNameAnalyzer(),
+            () => new LuceneDev7001_ProtectedFieldNameCodeFixProvider())
+        {
+            TestCode = testCode.ReplaceLineEndings(),
+            FixedCode = fixedCode.ReplaceLineEndings(),
+            ExpectedDiagnostics = { expected }
+        };
+
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev7xxx/TestLuceneDev7003_InterfaceNameCodeFixProvider.cs b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev7xxx/TestLuceneDev7003_InterfaceNameCodeFixProvider.cs
new file mode 100644
index 0000000..f28e012
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev7xxx/TestLuceneDev7003_InterfaceNameCodeFixProvider.cs
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev.CodeFixes;
+
+public class TestLuceneDev7003_InterfaceNameCodeFixProvider
+{
+    [Test]
+    public async Task RenamesInterfaceToIPrefix()
+    {
+        const string testCode =
+            """
+            public interface Walkable
+            {
+                void Walk();
+            }
+
+            public class Dog : Walkable
+            {
+                public void Walk() { }
+            }
+            """;
+
+        const string fixedCode =
+            """
+            public interface IWalkable
+            {
+                void Walk();
+            }
+
+            public class Dog : IWalkable
+            {
+                public void Walk() { }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7003_InterfaceName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Walkable")
+            .WithLocation(1, 18);
+
+        var test = new InjectableCodeFixTest(
+            () => new LuceneDev7003_InterfaceNameAnalyzer(),
+            () => new LuceneDev7003_InterfaceNameCodeFixProvider())
+        {
+            TestCode = testCode.ReplaceLineEndings(),
+            FixedCode = fixedCode.ReplaceLineEndings(),
+            ExpectedDiagnostics = { expected }
+        };
+
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task RenamesLowercaseIPrefixToUppercase()
+    {
+        const string testCode =
+            """
+            public interface iWalkable
+            {
+                void Walk();
+            }
+
+            public class Dog : iWalkable
+            {
+                public void Walk() { }
+            }
+            """;
+
+        const string fixedCode =
+            """
+            public interface IWalkable
+            {
+                void Walk();
+            }
+
+            public class Dog : IWalkable
+            {
+                public void Walk() { }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7003_InterfaceName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("iWalkable")
+            .WithLocation(1, 18);
+
+        var test = new InjectableCodeFixTest(
+            () => new LuceneDev7003_InterfaceNameAnalyzer(),
+            () => new LuceneDev7003_InterfaceNameCodeFixProvider())
+        {
+            TestCode = testCode.ReplaceLineEndings(),
+            FixedCode = fixedCode.ReplaceLineEndings(),
+            ExpectedDiagnostics = { expected }
+        };
+
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1009_PublicFieldsAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1009_PublicFieldsAnalyzer.cs
new file mode 100644
index 0000000..083dc4c
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1009_PublicFieldsAnalyzer.cs
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev1009_PublicFieldsAnalyzer
+{
+    [Test]
+    public async Task PublicInstanceField_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Value;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1009_PublicFields)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Value")
+            .WithLocation(3, 16);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1009_PublicFieldsAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PublicConstField_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public const int Value = 1;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1009_PublicFieldsAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PublicStaticField_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public static int Value;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1009_PublicFieldsAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PrivateField_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private int value;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1009_PublicFieldsAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1010_PropertyWithNoGetterAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1010_PropertyWithNoGetterAnalyzer.cs
new file mode 100644
index 0000000..86816a3
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1010_PropertyWithNoGetterAnalyzer.cs
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev1010_PropertyWithNoGetterAnalyzer
+{
+    [Test]
+    public async Task SetterOnlyProperty_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private int _value;
+                public int Value { set => _value = value; }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1010_PropertyWithNoGetter)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Value")
+            .WithLocation(4, 16);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1010_PropertyWithNoGetterAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PropertyWithGetterAndSetter_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Value { get; set; }
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1010_PropertyWithNoGetterAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task SetOnlyIndexer_Diagnostic()
+    {
+        // Reflection GetProperties includes indexers, so a set-only indexer is also flagged.
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private int _value;
+                public int this[int index] { set => _value = value; }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1010_PropertyWithNoGetter)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("this[]")
+            .WithLocation(4, 16);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1010_PropertyWithNoGetterAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1011_PropertyReturnsArrayAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1011_PropertyReturnsArrayAnalyzer.cs
new file mode 100644
index 0000000..46fd3b5
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1011_PropertyReturnsArrayAnalyzer.cs
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev1011_PropertyReturnsArrayAnalyzer
+{
+    [Test]
+    public async Task PropertyReturnsArray_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private readonly int[] _values = new int[1];
+                public int[] Values => _values;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1011_PropertyReturnsArray)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Values")
+            .WithLocation(4, 18);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1011_PropertyReturnsArrayAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PropertyReturnsNonArray_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Value => 0;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1011_PropertyReturnsArrayAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1012_MethodReturnsWritableArrayAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1012_MethodReturnsWritableArrayAnalyzer.cs
new file mode 100644
index 0000000..8e02a2b
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1012_MethodReturnsWritableArrayAnalyzer.cs
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev1012_MethodReturnsWritableArrayAnalyzer
+{
+    [Test]
+    public async Task ReturnsFieldDirectly_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private readonly int[] m_values = new int[1];
+                public int[] GetValues()
+                {
+                    return m_values;
+                }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1012_MethodReturnsWritableArray)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("GetValues")
+            .WithLocation(4, 18);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1012_MethodReturnsWritableArrayAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ReturnsFieldDirectly_ExpressionBodied_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private readonly int[] m_values = new int[1];
+                public int[] GetValues() => m_values;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1012_MethodReturnsWritableArray)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("GetValues")
+            .WithLocation(4, 18);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1012_MethodReturnsWritableArrayAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ReturnsClonedArray_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            using System.Linq;
+            public class MyClass
+            {
+                private readonly int[] m_values = new int[1];
+                public int[] GetValues() => m_values.ToArray();
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1012_MethodReturnsWritableArrayAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ReturnsNewArray_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int[] GetValues() => new int[1];
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1012_MethodReturnsWritableArrayAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ReturnsFieldDirectly_NullableArrayReturnType_Diagnostic()
+    {
+        const string testCode =
+            """
+            #nullable enable
+            public class MyClass
+            {
+                private readonly int[]? m_values = null;
+                public int[]? GetValues() => m_values;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1012_MethodReturnsWritableArray)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("GetValues")
+            .WithLocation(5, 19);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1012_MethodReturnsWritableArrayAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1013_NullableEnumAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1013_NullableEnumAnalyzer.cs
new file mode 100644
index 0000000..c9fabe4
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1013_NullableEnumAnalyzer.cs
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev1013_NullableEnumAnalyzer
+{
+    private const string EnumDef =
+        """
+        public enum Mode { First, Second }
+        """;
+
+    [Test]
+    public async Task NullableEnumProperty_Diagnostic()
+    {
+        string testCode =
+            $$"""
+            {{EnumDef}}
+            public class MyClass
+            {
+                public Mode? Mode { get; set; }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1013_NullableEnum)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Mode")
+            .WithLocation(4, 18);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1013_NullableEnumAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task NullableEnumParameter_Diagnostic()
+    {
+        string testCode =
+            $$"""
+            {{EnumDef}}
+            public class MyClass
+            {
+                public void Configure(Mode? mode) { }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1013_NullableEnum)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("mode")
+            .WithLocation(4, 33);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1013_NullableEnumAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task NonNullableEnumProperty_NoDiagnostic()
+    {
+        string testCode =
+            $$"""
+            {{EnumDef}}
+            public class MyClass
+            {
+                public Mode Mode { get; set; }
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1013_NullableEnumAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task NullableIntProperty_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int? Value { get; set; }
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1013_NullableEnumAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs
new file mode 100644
index 0000000..8c5de8b
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer.cs
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+// Note: LuceneDev1014 is disabled by default, but the analyzer test harness runs the analyzer
+// directly, so its diagnostics are still observed here.
+public class TestLuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer
+{
+    [Test]
+    public async Task MethodReturnsIEnumerable_Diagnostic()
+    {
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                public IEnumerable<int> GetItems() => null;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1014_MemberAcceptsOrReturnsIEnumerable)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("GetItems")
+            .WithLocation(4, 29);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task MethodAcceptsIEnumerableParameter_Diagnostic()
+    {
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                public void SetItems(IEnumerable<int> items) { }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1014_MemberAcceptsOrReturnsIEnumerable)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("items")
+            .WithLocation(4, 43);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task MethodReturnsIList_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                public IList<int> GetItems() => null;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1014_MemberAcceptsOrReturnsIEnumerableAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer.cs
new file mode 100644
index 0000000..54d7236
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer.cs
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev1xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer
+{
+    [Test]
+    public async Task MethodReturnsList_Diagnostic()
+    {
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                public List<int> GetItems() => null;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1015_MemberAcceptsOrReturnsListOrDictionary)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("GetItems", "List")
+            .WithLocation(4, 22);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task MethodAcceptsDictionaryParameter_Diagnostic()
+    {
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                public void SetItems(Dictionary<int, string> items) { }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev1015_MemberAcceptsOrReturnsListOrDictionary)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("items", "Dictionary")
+            .WithLocation(4, 50);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task MethodReturnsIList_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                public IList<int> GetItems() => null;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PrivateMethodReturnsList_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                private List<int> GetItems() => null;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PropertyWithOnlyNonPublicAccessor_NoDiagnostic()
+    {
+        // The original IsNonPrivateProperty requires a public accessor; a property whose only
+        // accessors are internal/private must not be flagged.
+        const string testCode =
+            """
+            using System.Collections.Generic;
+            public class MyClass
+            {
+                internal List<int> Items { get; set; }
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1015_MemberAcceptsOrReturnsListOrDictionaryAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7000_PrivateFieldNameAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7000_PrivateFieldNameAnalyzer.cs
new file mode 100644
index 0000000..912d52d
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7000_PrivateFieldNameAnalyzer.cs
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7000_PrivateFieldNameAnalyzer
+{
+    [Test]
+    public async Task TestEmptyFile()
+    {
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7000_PrivateFieldNameAnalyzer())
+        {
+            TestCode = ""
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    [TestCase("value")]
+    [TestCase("_value")]
+    [TestCase("MAX_VALUE")]
+    public async Task ValidNames_NoDiagnostic(string fieldName)
+    {
+        string testCode =
+            $$"""
+            public class MyClass
+            {
+                private int {{fieldName}};
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7000_PrivateFieldNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PascalCasePrivateField_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private int FieldValue;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7000_PrivateFieldName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("FieldValue")
+            .WithLocation(3, 17);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7000_PrivateFieldNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task InternalField_AlsoChecked_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                internal int FieldValue;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7000_PrivateFieldName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("FieldValue")
+            .WithLocation(3, 18);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7000_PrivateFieldNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PublicField_NotChecked_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int FieldValue;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7000_PrivateFieldNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7001_ProtectedFieldNameAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7001_ProtectedFieldNameAnalyzer.cs
new file mode 100644
index 0000000..f98fcae
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7001_ProtectedFieldNameAnalyzer.cs
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7001_ProtectedFieldNameAnalyzer
+{
+    [Test]
+    [TestCase("m_value")]
+    [TestCase("MAX_VALUE")]
+    public async Task ValidNames_NoDiagnostic(string fieldName)
+    {
+        string testCode =
+            $$"""
+            public class MyClass
+            {
+                protected int {{fieldName}};
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7001_ProtectedFieldNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ProtectedFieldWithoutPrefix_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                protected int count;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7001_ProtectedFieldName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("count")
+            .WithLocation(3, 19);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7001_ProtectedFieldNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ProtectedInternalField_AlsoChecked_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                protected internal int count;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7001_ProtectedFieldName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("count")
+            .WithLocation(3, 28);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7001_ProtectedFieldNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PrivateField_NotChecked_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                private int count;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7001_ProtectedFieldNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ProtectedFieldInSupportNamespace_Skipped_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            namespace Lucene.Net.Support
+            {
+                public class MyClass
+                {
+                    protected int count;
+                }
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7001_ProtectedFieldNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7002_MethodParameterNameAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7002_MethodParameterNameAnalyzer.cs
new file mode 100644
index 0000000..48700d5
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7002_MethodParameterNameAnalyzer.cs
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7002_MethodParameterNameAnalyzer
+{
+    [Test]
+    public async Task CamelCaseParameter_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Add(int first, int second) => first + second;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7002_MethodParameterNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PascalCaseParameter_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Add(int First, int second) => First + second;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7002_MethodParameterName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("First")
+            .WithLocation(3, 24);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7002_MethodParameterNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task TrailingUnderscoreParameter_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public void Foo(int value_) { }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7002_MethodParameterName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("value_")
+            .WithLocation(3, 25);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7002_MethodParameterNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7003_InterfaceNameAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7003_InterfaceNameAnalyzer.cs
new file mode 100644
index 0000000..38813b2
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7003_InterfaceNameAnalyzer.cs
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7003_InterfaceNameAnalyzer
+{
+    [Test]
+    [TestCase("IComparer")]
+    [TestCase("IList")]
+    public async Task ValidInterfaceName_NoDiagnostic(string name)
+    {
+        string testCode =
+            $$"""
+            public interface {{name}}
+            {
+                void DoWork();
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7003_InterfaceNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task InterfaceWithoutIPrefix_Diagnostic()
+    {
+        const string testCode =
+            """
+            public interface Comparer
+            {
+                void DoWork();
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7003_InterfaceName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Comparer")
+            .WithLocation(1, 18);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7003_InterfaceNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task GenericInterfaceWithIPrefix_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public interface IComparer<T>
+            {
+                int Compare(T a, T b);
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7003_InterfaceNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7004_ClassNameAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7004_ClassNameAnalyzer.cs
new file mode 100644
index 0000000..5fc3ecf
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7004_ClassNameAnalyzer.cs
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7004_ClassNameAnalyzer
+{
+    [Test]
+    public async Task PascalCaseClassName_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7004_ClassNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ClassNameLikeInterface_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class IMyClass
+            {
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7004_ClassName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("IMyClass")
+            .WithLocation(1, 14);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7004_ClassNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task LowerCaseClassName_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class myClass
+            {
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7004_ClassName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("myClass")
+            .WithLocation(1, 14);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7004_ClassNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task DelegateNameLikeInterface_Diagnostic()
+    {
+        // Delegates are CLR classes (reflection t.IsClass), so they follow the class-name rule.
+        const string testCode =
+            """
+            public delegate void IMyHandler();
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7004_ClassName)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("IMyHandler")
+            .WithLocation(1, 22);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7004_ClassNameAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ValidDelegateName_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public delegate void MyHandler();
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7004_ClassNameAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7005_MemberContainsComparerAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7005_MemberContainsComparerAnalyzer.cs
new file mode 100644
index 0000000..1aaa500
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7005_MemberContainsComparerAnalyzer.cs
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7005_MemberContainsComparerAnalyzer
+{
+    [Test]
+    public async Task MethodNamedComparer_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int GetComparer() => 0;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7005_MemberContainsComparerAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task MethodContainingComparator_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int GetComparator() => 0;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7005_MemberContainsComparer)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("GetComparator")
+            .WithLocation(3, 16);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7005_MemberContainsComparerAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task TypeNamedComparator_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyComparator
+            {
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7005_MemberContainsComparer)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("MyComparator")
+            .WithLocation(1, 14);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7005_MemberContainsComparerAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ProtectedFieldContainingComparator_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                protected int m_comparatorState;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7005_MemberContainsComparer)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("m_comparatorState")
+            .WithLocation(3, 19);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7005_MemberContainsComparerAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ExplicitInterfaceImplementationContainingComparator_Diagnostic()
+    {
+        // Matches the original reflection-based scan, which sees the explicit implementation
+        // under its qualified name (e.g. "IComparator.ComparatorMethod").
+        const string testCode =
+            """
+            public interface IComparator
+            {
+                void ComparatorMethod();
+            }
+
+            public class MyClass : IComparator
+            {
+                void IComparator.ComparatorMethod() { }
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7005_MemberContainsComparerAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics =
+            {
+                new DiagnosticResult(Descriptors.LuceneDev7005_MemberContainsComparer)
+                    .WithSeverity(DiagnosticSeverity.Warning)
+                    .WithArguments("IComparator")
+                    .WithLocation(1, 18),
+                new DiagnosticResult(Descriptors.LuceneDev7005_MemberContainsComparer)
+                    .WithSeverity(DiagnosticSeverity.Warning)
+                    .WithArguments("ComparatorMethod")
+                    .WithLocation(3, 10),
+                new DiagnosticResult(Descriptors.LuceneDev7005_MemberContainsComparer)
+                    .WithSeverity(DiagnosticSeverity.Warning)
+                    .WithArguments("IComparator.ComparatorMethod")
+                    .WithLocation(8, 22),
+            }
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7006_MemberNamedSizeAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7006_MemberNamedSizeAnalyzer.cs
new file mode 100644
index 0000000..ef09037
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7006_MemberNamedSizeAnalyzer.cs
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7006_MemberNamedSizeAnalyzer
+{
+    [Test]
+    public async Task PropertyNamedCount_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Count { get; set; }
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7006_MemberNamedSizeAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task PropertyNamedSize_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Size { get; set; }
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7006_MemberNamedSize)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Size")
+            .WithLocation(3, 16);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7006_MemberNamedSizeAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task ParameterlessMethodNamedSize_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Size() => 0;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7006_MemberNamedSize)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("Size")
+            .WithLocation(3, 16);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7006_MemberNamedSizeAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task MethodNamedSizeWithParameters_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class MyClass
+            {
+                public int Size(int dimension) => dimension;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7006_MemberNamedSizeAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7007_MemberContainsNonNetNumericAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7007_MemberContainsNonNetNumericAnalyzer.cs
new file mode 100644
index 0000000..c6353ee
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7007_MemberContainsNonNetNumericAnalyzer.cs
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7007_MemberContainsNonNetNumericAnalyzer
+{
+    [Test]
+    [TestCase("ReadInt32")]
+    [TestCase("Intern")]
+    [TestCase("GetPoint")]
+    public async Task NetNumericOrInnocuousNames_NoDiagnostic(string methodName)
+    {
+        string testCode =
+            $$"""
+            public class MyClass
+            {
+                public int {{methodName}}() => 0;
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7007_MemberContainsNonNetNumericAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    [TestCase("ReadLong", 16)]
+    [TestCase("ReadInt", 16)]
+    [TestCase("ReadFloat", 16)]
+    [TestCase("ReadShort", 16)]
+    public async Task NonNetNumericMethodName_Diagnostic(string methodName, int column)
+    {
+        string testCode =
+            $$"""
+            public class MyClass
+            {
+                public int {{methodName}}() => 0;
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7007_MemberContainsNonNetNumeric)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments(methodName)
+            .WithLocation(3, column);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7007_MemberContainsNonNetNumericAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+}
diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7008_TypeContainsNonNetNumericAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7008_TypeContainsNonNetNumericAnalyzer.cs
new file mode 100644
index 0000000..c80f8b0
--- /dev/null
+++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev7xxx/TestLuceneDev7008_TypeContainsNonNetNumericAnalyzer.cs
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Threading.Tasks;
+using Lucene.Net.CodeAnalysis.Dev.LuceneDev7xxx;
+using Lucene.Net.CodeAnalysis.Dev.TestUtilities;
+using Lucene.Net.CodeAnalysis.Dev.Utility;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Testing;
+using NUnit.Framework;
+
+namespace Lucene.Net.CodeAnalysis.Dev;
+
+public class TestLuceneDev7008_TypeContainsNonNetNumericAnalyzer
+{
+    [Test]
+    public async Task NetNumericTypeName_NoDiagnostic()
+    {
+        const string testCode =
+            """
+            public class Int64Reader
+            {
+            }
+            """;
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7008_TypeContainsNonNetNumericAnalyzer())
+        {
+            TestCode = testCode
+        };
+        await test.RunAsync();
+    }
+
+    [Test]
+    public async Task NonNetNumericTypeName_Diagnostic()
+    {
+        const string testCode =
+            """
+            public class LongReader
+            {
+            }
+            """;
+
+        var expected = new DiagnosticResult(Descriptors.LuceneDev7008_TypeContainsNonNetNumeric)
+            .WithSeverity(DiagnosticSeverity.Warning)
+            .WithArguments("LongReader")
+            .WithLocation(1, 14);
+
+        var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev7008_TypeContainsNonNetNumericAnalyzer())
+        {
+            TestCode = testCode,
+            ExpectedDiagnostics = { expected }
+        };
+        await test.RunAsync();
+    }
+}