仿真平台内核初版 -tlib库 包含<sparc arm riscv powerPC>
This commit is contained in:
13
tools/analyzers/Analyzers.csproj
Normal file
13
tools/analyzers/Analyzers.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.9.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
22
tools/analyzers/Analyzers.sln
Normal file
22
tools/analyzers/Analyzers.sln
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Analyzers", "Analyzers.csproj", "{3BD152FA-6A0A-45E1-A570-0E9B8488030A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3BD152FA-6A0A-45E1-A570-0E9B8488030A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3BD152FA-6A0A-45E1-A570-0E9B8488030A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3BD152FA-6A0A-45E1-A570-0E9B8488030A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3BD152FA-6A0A-45E1-A570-0E9B8488030A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
4
tools/analyzers/Directory.Build.targets
Normal file
4
tools/analyzers/Directory.Build.targets
Normal file
@@ -0,0 +1,4 @@
|
||||
<Project>
|
||||
<!-- Empty default so that the upper level Directory.Build.targets is not used. -->
|
||||
<!-- https://github.com/MicrosoftDocs/visualstudio-docs/blob/705d3b200e68e87d1070696cb6f483462c7be3e2/docs/msbuild/customize-your-build.md?plain=1#L42 -->
|
||||
</Project>
|
||||
247
tools/analyzers/RenodeClassMembersOrderAnalyzer.cs
Normal file
247
tools/analyzers/RenodeClassMembersOrderAnalyzer.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
//
|
||||
// Copyright (c) 2010-2025 Antmicro
|
||||
//
|
||||
// This file is licensed under the MIT License.
|
||||
// Full license text is available in 'licenses/MIT.txt'.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Antmicro.Renode.CustomAnalyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class RenodeClassMembersOrderAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||
ImmutableArray.Create(ClassMembersOrderRule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxTreeAction(CheckClassMembersOrder);
|
||||
}
|
||||
|
||||
public const string DiagnosticId = "renode_class_members_order";
|
||||
|
||||
private void CheckClassMembersOrder(SyntaxTreeAnalysisContext context)
|
||||
{
|
||||
var classes = context
|
||||
.Tree
|
||||
.GetRoot()
|
||||
.DescendantNodes()
|
||||
.Where(node => node.IsKind(SyntaxKind.ClassDeclaration));
|
||||
|
||||
foreach(var cls in classes)
|
||||
{
|
||||
var declarations = cls
|
||||
.ChildNodes()
|
||||
.OfType<MemberDeclarationSyntax>();
|
||||
|
||||
var currentMember = ClassMemberOrder.StaticConstructor;
|
||||
|
||||
foreach(var declaration in declarations)
|
||||
{
|
||||
if(TryGetClassMemberOrder(declaration, out var nextMember))
|
||||
{
|
||||
if(nextMember < currentMember)
|
||||
{
|
||||
var diagnostic = Diagnostic.Create(
|
||||
ClassMembersOrderRule,
|
||||
declaration.GetLocation(),
|
||||
new object[] {currentMember, nextMember}
|
||||
);
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
currentMember = nextMember;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetClassMemberOrder(MemberDeclarationSyntax node, out ClassMemberOrder classMemberOrder)
|
||||
{
|
||||
var ignoredModifiers = ImmutableHashSet.Create
|
||||
(
|
||||
"async",
|
||||
"extern",
|
||||
"internal",
|
||||
"override",
|
||||
"partial",
|
||||
"sealed",
|
||||
"unsafe",
|
||||
"virtual",
|
||||
"volatile"
|
||||
);
|
||||
|
||||
var modifiersOrder = new Dictionary<string, int>
|
||||
{
|
||||
{"public", 0},
|
||||
{"protected", 0},
|
||||
{"private", 0},
|
||||
{"static", 1},
|
||||
{"abstract", 2},
|
||||
{"readonly", 3},
|
||||
{"const", 3},
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var modifiers = node
|
||||
.Modifiers
|
||||
.OrderBy(mod => modifiersOrder.GetValueOrDefault(mod.ToString(), -1))
|
||||
.Aggregate("", (acc, val) => ignoredModifiers.Contains(val.ToString()) ? acc : $"{acc}_{val}");
|
||||
var id = $"{modifiers}_{node.Kind()}";
|
||||
|
||||
if(ClassMembersOrderMap.TryGetValue(id, out classMemberOrder))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable SA1509
|
||||
// SA1509 - No space before opening braces
|
||||
public static readonly Dictionary<string, ClassMemberOrder> ClassMembersOrderMap = new Dictionary<string, ClassMemberOrder>
|
||||
{
|
||||
{"_static_ConstructorDeclaration", ClassMemberOrder.StaticConstructor},
|
||||
{"_public_static_MethodDeclaration", ClassMemberOrder.PublicStaticMethod},
|
||||
{"_public_static_PropertyDeclaration", ClassMemberOrder.PublicStaticProperty},
|
||||
{"_public_static_explicit_OperatorDeclaration", ClassMemberOrder.PublicStaticExplicitOperator},
|
||||
{"_public_static_implicit_OperatorDeclaration", ClassMemberOrder.PublicStaticImplicitOperator},
|
||||
{"_public_static_FieldDeclaration", ClassMemberOrder.PublicStaticField},
|
||||
{"_public_ConstructorDeclaration", ClassMemberOrder.PublicConstructor},
|
||||
{"_public_MethodDeclaration", ClassMemberOrder.PublicMethod},
|
||||
{"_public_abstract_MethodDeclaration", ClassMemberOrder.PublicAbstractMethod},
|
||||
{"_public_PropertyDeclaration", ClassMemberOrder.PublicProperty},
|
||||
{"_public_abstract_PropertyDeclaration", ClassMemberOrder.PublicAbstractProperty},
|
||||
{"_public_abstract_readonly_PropertyDeclaration", ClassMemberOrder.PublicAbstractProperty},
|
||||
{"_public_EventFieldDeclaration", ClassMemberOrder.PublicEvent},
|
||||
{"_public_FieldDeclaration", ClassMemberOrder.PublicField},
|
||||
{"_public_readonly_FieldDeclaration", ClassMemberOrder.PublicReadonlyField},
|
||||
{"_public_const_FieldDeclaration", ClassMemberOrder.PublicConst},
|
||||
|
||||
{"_protected_static_MethodDeclaration", ClassMemberOrder.ProtectedStaticMethod},
|
||||
{"_protected_static_PropertyDeclaration", ClassMemberOrder.ProtectedStaticProperty},
|
||||
{"_protected_static_explicit_OperatorDeclaration", ClassMemberOrder.ProtectedStaticExplicitOperator},
|
||||
{"_protected_static_implicit_OperatorDeclaration", ClassMemberOrder.ProtectedStaticImplicitOperator},
|
||||
{"_protected_static_FieldDeclaration", ClassMemberOrder.ProtectedStaticField},
|
||||
{"_protected_ConstructorDeclaration", ClassMemberOrder.ProtectedConstructor},
|
||||
{"_protected_MethodDeclaration", ClassMemberOrder.ProtectedMethod},
|
||||
{"_protected_abstract_MethodDeclaration", ClassMemberOrder.ProtectedAbstractMethod},
|
||||
{"_protected_PropertyDeclaration", ClassMemberOrder.ProtectedProperty},
|
||||
{"_protected_abstract_PropertyDeclaration", ClassMemberOrder.ProtectedAbstractProperty},
|
||||
{"_protected_abstract_readonly_PropertyDeclaration", ClassMemberOrder.ProtectedAbstractProperty},
|
||||
{"_protected_EventFieldDeclaration", ClassMemberOrder.ProtectedEvent},
|
||||
{"_protected_FieldDeclaration", ClassMemberOrder.ProtectedField},
|
||||
{"_protected_readonly_FieldDeclaration", ClassMemberOrder.ProtectedReadonlyField},
|
||||
{"_protected_const_FieldDeclaration", ClassMemberOrder.ProtectedConst},
|
||||
|
||||
{"_private_static_MethodDeclaration", ClassMemberOrder.PrivateStaticMethod},
|
||||
{"_private_static_FieldDeclaration", ClassMemberOrder.PrivateStaticField},
|
||||
{"_private_ConstructorDeclaration", ClassMemberOrder.PrivateConstructor},
|
||||
{"_private_MethodDeclaration", ClassMemberOrder.PrivateMethod},
|
||||
{"_private_abstract_MethodDeclaration", ClassMemberOrder.PrivateAbstractMethod},
|
||||
{"_private_PropertyDeclaration", ClassMemberOrder.PrivateProperty},
|
||||
{"_private_abstract_PropertyDeclaration", ClassMemberOrder.PrivateAbstractProperty},
|
||||
{"_private_abstract_readonly_PropertyDeclaration", ClassMemberOrder.PrivateAbstractProperty},
|
||||
{"_private_FieldDeclaration", ClassMemberOrder.PrivateField},
|
||||
{"_private_readonly_FieldDeclaration", ClassMemberOrder.PrivateReadonlyField},
|
||||
{"_private_const_FieldDeclaration", ClassMemberOrder.PrivateConst},
|
||||
|
||||
{"_public_ClassDeclaration", ClassMemberOrder.PublicClass},
|
||||
{"_public_abstract_ClassDeclaration", ClassMemberOrder.PublicAbstractClass},
|
||||
{"_public_StructDeclaration", ClassMemberOrder.PublicStruct},
|
||||
{"_public_DelegateDeclaration", ClassMemberOrder.PublicDelegate},
|
||||
{"_public_EnumDeclaration", ClassMemberOrder.PublicEnum},
|
||||
|
||||
{"_protected_ClassDeclaration", ClassMemberOrder.ProtectedClass},
|
||||
{"_protected_abstract_ClassDeclaration", ClassMemberOrder.ProtectedAbstractClass},
|
||||
{"_protected_StructDeclaration", ClassMemberOrder.ProtectedStruct},
|
||||
{"_protected_DelegateDeclaration", ClassMemberOrder.ProtectedDelegate},
|
||||
{"_protected_EnumDeclaration", ClassMemberOrder.ProtectedEnum},
|
||||
|
||||
{"_private_ClassDeclaration", ClassMemberOrder.PrivateClass},
|
||||
{"_private_abstract_ClassDeclaration", ClassMemberOrder.PrivateAbstractClass},
|
||||
{"_private_StructDeclaration", ClassMemberOrder.PrivateStruct},
|
||||
{"_private_DelegateDeclaration", ClassMemberOrder.PrivateDelegate},
|
||||
{"_private_EnumDeclaration", ClassMemberOrder.PrivateEnum},
|
||||
};
|
||||
#pragma warning restore SA1509
|
||||
|
||||
private static readonly DiagnosticDescriptor ClassMembersOrderRule = new DiagnosticDescriptor(
|
||||
DiagnosticId,
|
||||
"Class Members Order",
|
||||
"Wrong Class Members Order: {1} should be placed before {0}",
|
||||
"Design",
|
||||
DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: false,
|
||||
description: "Checks if class members order is preserved."
|
||||
);
|
||||
|
||||
// Order of fields in this Enum is important.
|
||||
// It specifies allowed order of class members.
|
||||
public enum ClassMemberOrder
|
||||
{
|
||||
StaticConstructor,
|
||||
PublicStaticMethod,
|
||||
PublicStaticProperty,
|
||||
PublicStaticExplicitOperator,
|
||||
PublicStaticImplicitOperator,
|
||||
PublicStaticField,
|
||||
PublicConstructor,
|
||||
PublicMethod,
|
||||
PublicAbstractMethod,
|
||||
PublicProperty,
|
||||
PublicAbstractProperty,
|
||||
PublicEvent,
|
||||
PublicField,
|
||||
PublicReadonlyField,
|
||||
PublicConst,
|
||||
ProtectedStaticMethod,
|
||||
ProtectedStaticProperty,
|
||||
ProtectedStaticExplicitOperator,
|
||||
ProtectedStaticImplicitOperator,
|
||||
ProtectedStaticField,
|
||||
ProtectedConstructor,
|
||||
ProtectedMethod,
|
||||
ProtectedAbstractMethod,
|
||||
ProtectedProperty,
|
||||
ProtectedAbstractProperty,
|
||||
ProtectedEvent,
|
||||
ProtectedField,
|
||||
ProtectedReadonlyField,
|
||||
ProtectedConst,
|
||||
PrivateStaticMethod,
|
||||
PrivateStaticField,
|
||||
PrivateConstructor,
|
||||
PrivateMethod,
|
||||
PrivateAbstractMethod,
|
||||
PrivateProperty,
|
||||
PrivateAbstractProperty,
|
||||
PrivateField,
|
||||
PrivateReadonlyField,
|
||||
PrivateConst,
|
||||
PublicClass,
|
||||
PublicAbstractClass,
|
||||
PublicStruct,
|
||||
PublicDelegate,
|
||||
PublicEnum,
|
||||
ProtectedClass,
|
||||
ProtectedAbstractClass,
|
||||
ProtectedStruct,
|
||||
ProtectedDelegate,
|
||||
ProtectedEnum,
|
||||
PrivateClass,
|
||||
PrivateAbstractClass,
|
||||
PrivateStruct,
|
||||
PrivateDelegate,
|
||||
PrivateEnum,
|
||||
}
|
||||
}
|
||||
}
|
||||
217
tools/analyzers/RenodeClassMembersOrderFixProvider.cs
Normal file
217
tools/analyzers/RenodeClassMembersOrderFixProvider.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// Copyright (c) 2010-2025 Antmicro
|
||||
//
|
||||
// This file is licensed under the MIT License.
|
||||
// Full license text is available in 'licenses/MIT.txt'.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Editing;
|
||||
|
||||
namespace Antmicro.Renode.CustomAnalyzers
|
||||
{
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RenodeClassMembersOrderFixProvider)), Shared]
|
||||
public class RenodeClassMembersOrderFixProvider : CodeFixProvider
|
||||
{
|
||||
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
|
||||
ImmutableArray.Create(RenodeClassMembersOrderAnalyzer.DiagnosticId);
|
||||
|
||||
public override FixAllProvider GetFixAllProvider() =>
|
||||
FixAllProvider.Create(async (context, document, _) =>
|
||||
await FixDocumentClassMembersOrder(document, context.CancellationToken).ConfigureAwait(false));
|
||||
|
||||
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
var document = context.Document;
|
||||
// We have only one entry in FixableDiagnosticIds,
|
||||
// so there should be just one entry on the list.
|
||||
var diagnostic = context.Diagnostics.First();
|
||||
|
||||
var codeAction = CodeAction.Create(
|
||||
title: "Fix Renode class member order",
|
||||
createChangedDocument: c => FixSingleClassMemberOrder(document, diagnostic, c),
|
||||
equivalenceKey: "RenodeClassMemberOrder"
|
||||
);
|
||||
|
||||
context.RegisterCodeFix(codeAction, diagnostic);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task<Document> FixDocumentClassMembersOrder(
|
||||
Document document,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var editor = await DocumentEditor.CreateAsync(document, cancellationToken);
|
||||
|
||||
// This fix provider is capable of fixing members order also within nested calsses, on arbitrary level.
|
||||
// In order to do so, it must handle nested classes first. Since we list members recursively
|
||||
// in document prefix order, we just have to reverse list to start with nested classes.
|
||||
var classes = editor
|
||||
.OriginalRoot
|
||||
.DescendantNodes()
|
||||
.Where(node => node.IsKind(SyntaxKind.ClassDeclaration))
|
||||
.Reverse();
|
||||
|
||||
foreach(var cls in classes)
|
||||
{
|
||||
var currentRoot = editor.GetChangedRoot();
|
||||
|
||||
var declarations = cls
|
||||
.ChildNodes()
|
||||
.OfType<MemberDeclarationSyntax>();
|
||||
|
||||
// We first find all misplaced members and remove them from tree.
|
||||
// When we reinsert removed nodes to tree, they are treated as new nodes, so we cannot reference them
|
||||
// in tree operations (i.e. we cannot use them as reference when inserting new nodes).
|
||||
// We also remember all well ordered nodes to use them as references when we reinsert misplaced nodes.
|
||||
var wellOrderedMembers = new List<MemberDeclarationSyntax>();
|
||||
var misplacedMembers = new List<MemberDeclarationSyntax>();
|
||||
|
||||
var currentMember = RenodeClassMembersOrderAnalyzer.ClassMemberOrder.StaticConstructor;
|
||||
foreach(var declaration in declarations)
|
||||
{
|
||||
if(RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(declaration, out var nextMember))
|
||||
{
|
||||
if(nextMember < currentMember)
|
||||
{
|
||||
misplacedMembers.Add(declaration);
|
||||
editor.RemoveNode(declaration, SyntaxRemoveOptions.KeepNoTrivia);
|
||||
}
|
||||
else
|
||||
{
|
||||
wellOrderedMembers.Add(declaration);
|
||||
currentMember = nextMember;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This class already satisfies Renode class members order rule
|
||||
if(misplacedMembers.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We know that this class content will be modified, so we want to track this class
|
||||
// in case we need to move it later
|
||||
editor.TrackNode(cls);
|
||||
|
||||
// At this point all members stored in wellOrderedMembers and misplacedMembers
|
||||
// have corresponding ClassMemberOrder, so we no longer need to check every call
|
||||
// to RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder.
|
||||
misplacedMembers.Sort((lhs, rhs) =>
|
||||
{
|
||||
RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(lhs, out var lhsOrder);
|
||||
RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(rhs, out var rhsOrder);
|
||||
return lhsOrder.CompareTo(rhsOrder);
|
||||
});
|
||||
|
||||
// If we are here, we know that there is at least one correct memeber and at least one misplaced member.
|
||||
// So calling MoveNext on Enumerator is safe.
|
||||
var correctMemberEnumerator = wellOrderedMembers.GetEnumerator();
|
||||
correctMemberEnumerator.MoveNext();
|
||||
|
||||
var correctMember = correctMemberEnumerator.Current;
|
||||
RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(correctMember, out var correctOrder);
|
||||
|
||||
// We keep last valid order to easily break loop
|
||||
RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(wellOrderedMembers.Last(), out var lastCorrectOrder);
|
||||
|
||||
foreach(var misplacedMember in misplacedMembers)
|
||||
{
|
||||
RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(misplacedMember, out var misplacedOrder);
|
||||
if(misplacedOrder > lastCorrectOrder)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while(misplacedOrder > correctOrder)
|
||||
{
|
||||
correctMemberEnumerator.MoveNext();
|
||||
correctMember = correctMemberEnumerator.Current;
|
||||
RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(correctMember, out correctOrder);
|
||||
}
|
||||
|
||||
var upToDateMember = currentRoot.GetCurrentNode(misplacedMember) ?? misplacedMember;
|
||||
editor.InsertBefore(correctMember, upToDateMember);
|
||||
}
|
||||
|
||||
// correctMember now points to the last valid member.
|
||||
// We have to perform InsertAfter on remaining misplaced nodes
|
||||
// in reverse order to keep valid members order.
|
||||
misplacedMembers.Reverse();
|
||||
foreach(var misplacedMember in misplacedMembers)
|
||||
{
|
||||
RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(misplacedMember, out var misplacedOrder);
|
||||
if(misplacedOrder <= lastCorrectOrder)
|
||||
{
|
||||
break;
|
||||
}
|
||||
var upToDateMember = currentRoot.GetCurrentNode(misplacedMember) ?? misplacedMember;
|
||||
editor.InsertAfter(correctMember, upToDateMember);
|
||||
}
|
||||
}
|
||||
|
||||
return editor.GetChangedDocument();
|
||||
}
|
||||
|
||||
private async Task<Document> FixSingleClassMemberOrder(
|
||||
Document document,
|
||||
Diagnostic diagnostic,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var editor = await DocumentEditor.CreateAsync(document, cancellationToken);
|
||||
var root = editor.OriginalRoot;
|
||||
|
||||
var misplacedDeclaration = root
|
||||
.FindToken(diagnostic.Location.SourceSpan.Start)
|
||||
.Parent
|
||||
.AncestorsAndSelf()
|
||||
.OfType<MemberDeclarationSyntax>()
|
||||
.First();
|
||||
|
||||
if(!RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(misplacedDeclaration, out var misplacedOrder))
|
||||
{
|
||||
return document;
|
||||
}
|
||||
|
||||
var classMembers = misplacedDeclaration
|
||||
.Parent
|
||||
.ChildNodes()
|
||||
.OfType<MemberDeclarationSyntax>();
|
||||
|
||||
editor.RemoveNode(misplacedDeclaration, SyntaxRemoveOptions.KeepNoTrivia);
|
||||
|
||||
var successor = classMembers
|
||||
.FirstOrDefault(
|
||||
node => RenodeClassMembersOrderAnalyzer.TryGetClassMemberOrder(node, out var correctOrder) && misplacedOrder <= correctOrder);
|
||||
|
||||
// If there isn't any member, that is greater or equal than misplaced declaration,
|
||||
// then it must be last declaration.
|
||||
if(successor == null)
|
||||
{
|
||||
editor.InsertAfter(classMembers.Last(), misplacedDeclaration);
|
||||
}
|
||||
else
|
||||
{
|
||||
editor.InsertBefore(successor, misplacedDeclaration);
|
||||
}
|
||||
|
||||
return editor.GetChangedDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
156
tools/analyzers/RenodeCopyrightAnalyzer.cs
Normal file
156
tools/analyzers/RenodeCopyrightAnalyzer.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// Copyright (c) 2010-2025 Antmicro
|
||||
//
|
||||
// This file is licensed under the MIT License.
|
||||
// Full license text is available in 'licenses/MIT.txt'.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Antmicro.Renode.CustomAnalyzers
|
||||
{
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class RenodeCopyrightAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||
ImmutableArray.Create(CopyrightDateRule, CopyrightFormatRule);
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxTreeAction(CheckCopyrights);
|
||||
}
|
||||
|
||||
private void CheckCopyrights(SyntaxTreeAnalysisContext context)
|
||||
{
|
||||
var comments = context
|
||||
.Tree
|
||||
.GetRoot()
|
||||
.DescendantTrivia()
|
||||
.Where(node => node.IsKind(SyntaxKind.SingleLineCommentTrivia))
|
||||
.ToArray();
|
||||
|
||||
var commentIdx = 0;
|
||||
for(var templateIdx = 0; templateIdx < CopyrightTemplate.Length; ++templateIdx)
|
||||
{
|
||||
// We don't have any more comments to check
|
||||
if(commentIdx >= comments.Length)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, location: null));
|
||||
return;
|
||||
}
|
||||
|
||||
var comment = comments[commentIdx];
|
||||
var content = comment.ToString();
|
||||
var commentLine = comment
|
||||
.GetLocation()
|
||||
.GetLineSpan()
|
||||
.StartLinePosition
|
||||
.Line;
|
||||
|
||||
// copyright line might be correct, but it's on wrong line
|
||||
if(commentLine != commentIdx)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, location: null));
|
||||
}
|
||||
|
||||
switch(templateIdx)
|
||||
{
|
||||
case AntmicroCopyrightLine:
|
||||
CheckAntmicroCopyrights(context, comment);
|
||||
break;
|
||||
case AdditionalCopyrightLine:
|
||||
if(CheckAdditionalCopyrights(comment))
|
||||
{
|
||||
// We found additional copyright, but we adjust templateIdx
|
||||
// because we don't know how many there will be.
|
||||
templateIdx--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We end up pointing to the line after additional copyrights.
|
||||
// Let's adjust pointer to not skip any line.
|
||||
commentIdx--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(content != CopyrightTemplate[templateIdx])
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, comment.GetLocation()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
commentIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckAntmicroCopyrights(SyntaxTreeAnalysisContext context, SyntaxTrivia commentNode)
|
||||
{
|
||||
var content = commentNode.ToString();
|
||||
var regex = new Regex(CopyrightTemplate[AntmicroCopyrightLine]);
|
||||
var match = regex.Match(content);
|
||||
if(match.Success)
|
||||
{
|
||||
var copyrightYear = match.Groups[1].Value;
|
||||
var currentYear = DateTime.Today.Year.ToString();
|
||||
if(copyrightYear != currentYear)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(CopyrightDateRule, commentNode.GetLocation()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, commentNode.GetLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckAdditionalCopyrights(SyntaxTrivia commentNode)
|
||||
{
|
||||
var content = commentNode.ToString();
|
||||
var regex = new Regex(CopyrightTemplate[AdditionalCopyrightLine]);
|
||||
var match = regex.Match(content);
|
||||
return match.Success;
|
||||
}
|
||||
|
||||
private static readonly DiagnosticDescriptor CopyrightDateRule = new DiagnosticDescriptor(
|
||||
"renode_copyright",
|
||||
"Copyright Date",
|
||||
"Copyright is not up to date",
|
||||
"License",
|
||||
DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: false,
|
||||
description: "Ensure that copyrights are up to date."
|
||||
);
|
||||
|
||||
private static readonly DiagnosticDescriptor CopyrightFormatRule = new DiagnosticDescriptor(
|
||||
"renode_copyright",
|
||||
"Copyright Format",
|
||||
"Wrong copyright format",
|
||||
"License",
|
||||
DiagnosticSeverity.Warning,
|
||||
isEnabledByDefault: false,
|
||||
description: "Ensure that copyrights are in correct format."
|
||||
);
|
||||
|
||||
private static readonly string[] CopyrightTemplate = {
|
||||
"//",
|
||||
@"// Copyright \(c\) 2010-(\d+) Antmicro",
|
||||
@"// Copyright \(c\) .*",
|
||||
"//",
|
||||
"// This file is licensed under the MIT License.",
|
||||
"// Full license text is available in 'licenses/MIT.txt'.",
|
||||
"//"
|
||||
};
|
||||
|
||||
private const int AntmicroCopyrightLine = 1;
|
||||
private const int AdditionalCopyrightLine = 2;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user