Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .globalconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
roslyn_correctness.assembly_reference_validation = relaxed
Comment thread
paulirwin marked this conversation as resolved.
Outdated

Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,7 @@ under the License.
<value>Use {0}</value>
<comment>Title for code fix; {0} is the code element to utilize.</comment>
</data>
<data name="MakeXInternal" xml:space="preserve">
<value>Make {0} internal</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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.CSharp.Syntax;
using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using SyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind;

namespace Lucene.Net.CodeAnalysis.Dev;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(LuceneDev1005_LuceneNetSupportPublicTypesCsCodeFixProvider)), Shared]
public class LuceneDev1005_LuceneNetSupportPublicTypesCsCodeFixProvider : CodeFixProvider
Comment thread
paulirwin marked this conversation as resolved.
Outdated
{
// Specify the diagnostic IDs of analyzers that are expected to be linked.
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
[Descriptors.LuceneDev1005_LuceneNetSupportPublicTypes.Id];

public override FixAllProvider? GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
// We link only one diagnostic and assume there is only one diagnostic in the context.
var diagnostic = context.Diagnostics.Single();

// 'SourceSpan' of 'Location' is the highlighted area. We're going to use this area to find the 'SyntaxNode' to rename.
var diagnosticSpan = diagnostic.Location.SourceSpan;

// Get the root of Syntax Tree that contains the highlighted diagnostic.
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

// Find SyntaxNode corresponding to the diagnostic.
var diagnosticNode = root?.FindNode(diagnosticSpan);

if (diagnosticNode is MemberDeclarationSyntax declaration)
{
var name = declaration switch
{
BaseTypeDeclarationSyntax baseTypeDeclaration => baseTypeDeclaration.Identifier.ToString(),
DelegateDeclarationSyntax delegateDeclaration => delegateDeclaration.Identifier.ToString(),
_ => null
};

if (name == null)
{
return;
}

// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeActionHelper.CreateFromResource(
CodeFixResources.MakeXInternal,
createChangedSolution: c => MakeDeclarationInternal(context.Document, declaration, c),
"MakeDeclarationInternal",
name),
diagnostic);
}
}

private async Task<Solution> MakeDeclarationInternal(Document document,
MemberDeclarationSyntax memberDeclaration,
CancellationToken cancellationToken)
{
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
if (syntaxRoot == null) return document.Project.Solution;

// Remove existing accessibility modifiers
var newModifiers = SyntaxFactory.TokenList(
memberDeclaration.Modifiers
.Where(modifier => !modifier.IsKind(SyntaxKind.PrivateKeyword) &&
!modifier.IsKind(SyntaxKind.ProtectedKeyword) &&
!modifier.IsKind(SyntaxKind.InternalKeyword) &&
!modifier.IsKind(SyntaxKind.PublicKeyword))
).Insert(0, SyntaxFactory.Token(SyntaxKind.InternalKeyword)); // Ensure 'internal' is the first modifier

var newMemberDeclaration = memberDeclaration.WithModifiers(newModifiers);
var newRoot = syntaxRoot.ReplaceNode(memberDeclaration, newMemberDeclaration);
return document.Project.Solution.WithDocumentSyntaxRoot(document.Id, newRoot);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,18 @@ public static CodeAction CreateFromResource(
var title = string.Format(resourceValue, args);
return CodeAction.Create(title, createChangedDocument, equivalenceKey);
}

/// <summary>
/// Create a CodeAction using a resource string and formatting arguments.
/// </summary>
public static CodeAction CreateFromResource(
string resourceValue,
Func<CancellationToken, Task<Solution>> createChangedSolution,
string equivalenceKey,
params object[] args)
{
var title = string.Format(resourceValue, args);
return CodeAction.Create(title, createChangedSolution, equivalenceKey);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ under the License.
<_PackageVersionPropsFilePath>$(_NuGetPackageOutputPath)\Lucene.Net.CodeAnalysis.Dev.Version.props</_PackageVersionPropsFilePath>
<!-- We install the analyzer package in a local directory so we don't pollute the
.nuget cache on the dev machine with temporary builds -->
<RestorePackagesPath>obj\LocalNuGetPackages</RestorePackagesPath>
<_RestorePackagesPath>$(RestorePackagesPath)\lucene.net.codeanalsis.dev</_RestorePackagesPath>
<RestorePackagesPath>obj/LocalNuGetPackages</RestorePackagesPath>
<_RestorePackagesPath>$(RestorePackagesPath)/lucene.net.codeanalysis.dev</_RestorePackagesPath>
</PropertyGroup>

<PropertyGroup Condition="Exists('$(_NuGetPackageOutputPath)')">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Lucene.Net.Support.BlockScoped
{
public class PublicClass
{
}

public interface PublicInterface
{
}

public enum PublicEnum
{
}

public delegate void PublicDelegate();

public struct PublicStruct
{
}

public record PublicRecord
{
}

public record struct PublicRecordStruct
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Lucene.Net.Support;

public class PublicClass
{
}

public interface PublicInterface
{
}

public enum PublicEnum
{
}

public delegate void PublicDelegate();

public struct PublicStruct
{
}

public record PublicRecord
{
}

public record struct PublicRecordStruct
{
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
### New Rules

Rule ID | Category | Severity | Notes
Rule ID | Category | Severity | Notes
---------------|----------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------
LuceneDev1005 | Design | Warning | Types in the Lucene.Net.Support namespace should not be public
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
using Lucene.Net.CodeAnalysis.Dev.Utility;

namespace Lucene.Net.CodeAnalysis.Dev
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class LuceneDev1005_LuceneNetSupportPublicTypesCSCodeAnalyzer : DiagnosticAnalyzer
Comment thread
paulirwin marked this conversation as resolved.
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Descriptors.LuceneDev1005_LuceneNetSupportPublicTypes];

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeSyntax,
SyntaxKind.ClassDeclaration,
SyntaxKind.EnumDeclaration,
SyntaxKind.InterfaceDeclaration,
SyntaxKind.RecordDeclaration,
SyntaxKind.StructDeclaration,
SyntaxKind.RecordStructDeclaration,
SyntaxKind.DelegateDeclaration);
}

private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
// any public types in the Lucene.Net.Support (or child) namespace should raise the diagnostic
if (context.Node.Parent is not BaseNamespaceDeclarationSyntax namespaceDeclarationSyntax
|| !namespaceDeclarationSyntax.Name.ToString().StartsWith("Lucene.Net.Support"))
{
return;
}

if (context.Node is DelegateDeclarationSyntax delegateDeclarationSyntax
&& delegateDeclarationSyntax.Modifiers.Any(SyntaxKind.PublicKeyword))
{
const string typeKind = "Delegate";
context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1005_LuceneNetSupportPublicTypes, delegateDeclarationSyntax.GetLocation(), typeKind, delegateDeclarationSyntax.Identifier.ToString()));
}
else if (context.Node is BaseTypeDeclarationSyntax baseTypeDeclarationSyntax
&& baseTypeDeclarationSyntax.Modifiers.Any(SyntaxKind.PublicKeyword))
{
var typeKind = context.Node switch
{
ClassDeclarationSyntax => "Class",
EnumDeclarationSyntax => "Enum",
InterfaceDeclarationSyntax => "Interface",
RecordDeclarationSyntax record when record.ClassOrStructKeyword.IsKind(SyntaxKind.StructKeyword) => "Record struct",
RecordDeclarationSyntax => "Record",
StructDeclarationSyntax => "Struct",
_ => "Type", // should not happen
};

context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1005_LuceneNetSupportPublicTypes, baseTypeDeclarationSyntax.GetLocation(), typeKind, baseTypeDeclarationSyntax.Identifier.ToString()));
}
}
}
}
9 changes: 9 additions & 0 deletions src/Lucene.Net.CodeAnalysis.Dev/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,13 @@ under the License.
<value>Methods that return array types should be analyzed to determine whether they are better suited to be one or more out parameters or to return a ValueTuple</value>
<comment>The title of the diagnostic.</comment>
</data>
<data name="LuceneDev1005_AnalyzerTitle" xml:space="preserve">
<value>Types in the Support namespace should not be public</value>
</data>
<data name="LuceneDev1005_AnalyzerDescription" xml:space="preserve">
<value>Types in the Lucene.Net.Support namespace should not be public.</value>
</data>
<data name="LuceneDev1005_AnalyzerMessageFormat" xml:space="preserve">
<value>{0} '{1}' should not have public accessibility in the Support namespace</value>
</data>
</root>
8 changes: 2 additions & 6 deletions src/Lucene.Net.CodeAnalysis.Dev/Utility/Category.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
* 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
*
Comment thread
paulirwin marked this conversation as resolved.
*
* 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
Expand All @@ -17,10 +17,6 @@
* under the License.
*/

using System;
using System.Collections.Generic;
using System.Text;

namespace Lucene.Net.CodeAnalysis.Dev.Utility
{
public enum Category
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
* 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
Expand Down Expand Up @@ -63,5 +63,12 @@ public static partial class Descriptors
Design,
Warning
);

public static readonly DiagnosticDescriptor LuceneDev1005_LuceneNetSupportPublicTypes =
Diagnostic(
"LuceneDev1005",
Design,
Warning
);
}
}
Loading
Loading