1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Linq; 10 using System.Collections.Immutable; 11 using System.Text.RegularExpressions; 12 using Microsoft.CodeAnalysis; 13 using Microsoft.CodeAnalysis.CSharp; 14 using Microsoft.CodeAnalysis.Diagnostics; 15 16 namespace Antmicro.Renode.CustomAnalyzers 17 { 18 [DiagnosticAnalyzer(LanguageNames.CSharp)] 19 public class RenodeCopyrightAnalyzer : DiagnosticAnalyzer 20 { 21 public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => 22 ImmutableArray.Create(CopyrightDateRule, CopyrightFormatRule); 23 Initialize(AnalysisContext context)24 public override void Initialize(AnalysisContext context) 25 { 26 context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); 27 context.EnableConcurrentExecution(); 28 context.RegisterSyntaxTreeAction(CheckCopyrights); 29 } 30 CheckCopyrights(SyntaxTreeAnalysisContext context)31 private void CheckCopyrights(SyntaxTreeAnalysisContext context) 32 { 33 var comments = context 34 .Tree 35 .GetRoot() 36 .DescendantTrivia() 37 .Where(node => node.IsKind(SyntaxKind.SingleLineCommentTrivia)) 38 .ToArray(); 39 40 var commentIdx = 0; 41 for(var templateIdx = 0; templateIdx < CopyrightTemplate.Length; ++templateIdx) 42 { 43 // We don't have any more comments to check 44 if(commentIdx >= comments.Length) 45 { 46 context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, location: null)); 47 return; 48 } 49 50 var comment = comments[commentIdx]; 51 var content = comment.ToString(); 52 var commentLine = comment 53 .GetLocation() 54 .GetLineSpan() 55 .StartLinePosition 56 .Line; 57 58 // copyright line might be correct, but it's on wrong line 59 if(commentLine != commentIdx) 60 { 61 context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, location: null)); 62 } 63 64 switch(templateIdx) 65 { 66 case AntmicroCopyrightLine: 67 CheckAntmicroCopyrights(context, comment); 68 break; 69 case AdditionalCopyrightLine: 70 if(CheckAdditionalCopyrights(comment)) 71 { 72 // We found additional copyright, but we adjust templateIdx 73 // because we don't know how many there will be. 74 templateIdx--; 75 } 76 else 77 { 78 // We end up pointing to the line after additional copyrights. 79 // Let's adjust pointer to not skip any line. 80 commentIdx--; 81 } 82 break; 83 default: 84 if(content != CopyrightTemplate[templateIdx]) 85 { 86 context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, comment.GetLocation())); 87 } 88 break; 89 } 90 commentIdx++; 91 } 92 } 93 CheckAntmicroCopyrights(SyntaxTreeAnalysisContext context, SyntaxTrivia commentNode)94 private void CheckAntmicroCopyrights(SyntaxTreeAnalysisContext context, SyntaxTrivia commentNode) 95 { 96 var content = commentNode.ToString(); 97 var regex = new Regex(CopyrightTemplate[AntmicroCopyrightLine]); 98 var match = regex.Match(content); 99 if(match.Success) 100 { 101 var copyrightYear = match.Groups[1].Value; 102 var currentYear = DateTime.Today.Year.ToString(); 103 if(copyrightYear != currentYear) 104 { 105 context.ReportDiagnostic(Diagnostic.Create(CopyrightDateRule, commentNode.GetLocation())); 106 } 107 } 108 else 109 { 110 context.ReportDiagnostic(Diagnostic.Create(CopyrightFormatRule, commentNode.GetLocation())); 111 } 112 } 113 CheckAdditionalCopyrights(SyntaxTrivia commentNode)114 private bool CheckAdditionalCopyrights(SyntaxTrivia commentNode) 115 { 116 var content = commentNode.ToString(); 117 var regex = new Regex(CopyrightTemplate[AdditionalCopyrightLine]); 118 var match = regex.Match(content); 119 return match.Success; 120 } 121 122 private static readonly DiagnosticDescriptor CopyrightDateRule = new DiagnosticDescriptor( 123 "renode_copyright", 124 "Copyright Date", 125 "Copyright is not up to date", 126 "License", 127 DiagnosticSeverity.Warning, 128 isEnabledByDefault: false, 129 description: "Ensure that copyrights are up to date." 130 ); 131 132 private static readonly DiagnosticDescriptor CopyrightFormatRule = new DiagnosticDescriptor( 133 "renode_copyright", 134 "Copyright Format", 135 "Wrong copyright format", 136 "License", 137 DiagnosticSeverity.Warning, 138 isEnabledByDefault: false, 139 description: "Ensure that copyrights are in correct format." 140 ); 141 142 private static readonly string[] CopyrightTemplate = { 143 "//", 144 @"// Copyright \(c\) 2010-(\d+) Antmicro", 145 @"// Copyright \(c\) .*", 146 "//", 147 "// This file is licensed under the MIT License.", 148 "// Full license text is available in 'licenses/MIT.txt'.", 149 "//" 150 }; 151 152 private const int AntmicroCopyrightLine = 1; 153 private const int AdditionalCopyrightLine = 2; 154 } 155 } 156