diff --git a/analyzers/src/SonarAnalyzer.Common/AnalysisContext/SonarCompilationStartAnalysisContext.cs b/analyzers/src/SonarAnalyzer.Common/AnalysisContext/SonarCompilationStartAnalysisContext.cs index 1e38f8ace02..bd0373b0677 100644 --- a/analyzers/src/SonarAnalyzer.Common/AnalysisContext/SonarCompilationStartAnalysisContext.cs +++ b/analyzers/src/SonarAnalyzer.Common/AnalysisContext/SonarCompilationStartAnalysisContext.cs @@ -32,6 +32,9 @@ internal SonarCompilationStartAnalysisContext(SonarAnalysisContext analysisConte public void RegisterSymbolAction(Action action, params SymbolKind[] symbolKinds) => Context.RegisterSymbolAction(x => action(new(AnalysisContext, x)), symbolKinds); + public void RegisterSemanticModelAction(Action action) => + Context.RegisterSemanticModelAction(x => action(new(AnalysisContext, x))); + public void RegisterCompilationEndAction(Action action) => Context.RegisterCompilationEndAction(x => action(new(AnalysisContext, x))); diff --git a/analyzers/src/SonarAnalyzer.Common/AnalysisContext/SonarSematicModelReportingContext.cs b/analyzers/src/SonarAnalyzer.Common/AnalysisContext/SonarSematicModelReportingContext.cs new file mode 100644 index 00000000000..7ce1c496f3b --- /dev/null +++ b/analyzers/src/SonarAnalyzer.Common/AnalysisContext/SonarSematicModelReportingContext.cs @@ -0,0 +1,35 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2023 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarAnalyzer.AnalysisContext; + +public sealed class SonarSematicModelReportingContext : SonarTreeReportingContextBase +{ + public override SyntaxTree Tree => SemanticModel.SyntaxTree; + public override Compilation Compilation => Context.SemanticModel.Compilation; + public override AnalyzerOptions Options => Context.Options; + public override CancellationToken Cancel => Context.CancellationToken; + public SemanticModel SemanticModel => Context.SemanticModel; + + internal SonarSematicModelReportingContext(SonarAnalysisContext analysisContext, SemanticModelAnalysisContext context) : base(analysisContext, context) { } + + private protected override ReportingContext CreateReportingContext(Diagnostic diagnostic) => + new(this, diagnostic); +} diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/ReportingContext.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/ReportingContext.cs index 37ba075650c..a5f2990b702 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/ReportingContext.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/ReportingContext.cs @@ -43,6 +43,9 @@ public ReportingContext(SonarSymbolReportingContext context, Diagnostic diagnost public ReportingContext(SonarCodeBlockReportingContext context, Diagnostic diagnostic) : this(diagnostic, context.Context.ReportDiagnostic, context.Compilation, context.Tree) { } + public ReportingContext(SonarSematicModelReportingContext context, Diagnostic diagnostic) + : this(diagnostic, context.Context.ReportDiagnostic, context.Compilation, context.Tree) { } + private ReportingContext(Diagnostic diagnostic, Action roslynReportDiagnostic, Compilation compilation, diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/UtilityAnalyzerBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/UtilityAnalyzerBase.cs index 0fe145f7d57..9ccc48ce48f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/UtilityAnalyzerBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/Utilities/UtilityAnalyzerBase.cs @@ -50,22 +50,23 @@ internal static TextRange GetTextRange(FileLinePositionSpan lineSpan) => EndOffset = lineSpan.EndLinePosition.Character }; - protected void ReadParameters(SonarCompilationReportingContext c) + protected void ReadParameters(SonarCompilationStartAnalysisContext context) { - var settings = c.Options.ParseSonarLintXmlSettings(); - var outPath = c.ProjectConfiguration().OutPath; + var settings = context.Options.ParseSonarLintXmlSettings(); + var outPath = context.ProjectConfiguration().OutPath; // For backward compatibility with S4MSB <= 5.0 - if (outPath == null && c.Options.ProjectOutFolderPath() is { } projectOutFolderAdditionalFile) + if (outPath == null && context.Options.ProjectOutFolderPath() is { } projectOutFolderAdditionalFile) { outPath = projectOutFolderAdditionalFile.GetText().ToString().TrimEnd(); } if (settings.Any() && !string.IsNullOrEmpty(outPath)) { - IgnoreHeaderComments = PropertiesHelper.ReadIgnoreHeaderCommentsProperty(settings, c.Compilation.Language); - AnalyzeGeneratedCode = PropertiesHelper.ReadAnalyzeGeneratedCodeProperty(settings, c.Compilation.Language); - OutPath = Path.Combine(outPath, c.Compilation.Language == LanguageNames.CSharp ? "output-cs" : "output-vbnet"); + var language = context.Compilation.Language; + IgnoreHeaderComments = PropertiesHelper.ReadIgnoreHeaderCommentsProperty(settings, language); + AnalyzeGeneratedCode = PropertiesHelper.ReadAnalyzeGeneratedCodeProperty(settings, language); + OutPath = Path.Combine(outPath, language == LanguageNames.CSharp ? "output-cs" : "output-vbnet"); IsAnalyzerEnabled = true; - IsTestProject = c.IsTestProject(); + IsTestProject = context.IsTestProject(); } } } @@ -87,18 +88,26 @@ public abstract class UtilityAnalyzerBase : UtilityAnalyz protected UtilityAnalyzerBase(string diagnosticId, string title) : base(diagnosticId, title) { } protected sealed override void Initialize(SonarAnalysisContext context) => - context.RegisterCompilationAction(c => + context.RegisterCompilationStartAction(startContext => + { + ReadParameters(startContext); + if (!IsAnalyzerEnabled) + { + return; + } + + var treeMessages = new List(); + startContext.RegisterSemanticModelAction(modelContext => { - ReadParameters(c); - if (!IsAnalyzerEnabled) + if (ShouldGenerateMetrics(modelContext)) { - return; + treeMessages.Add(CreateMessage(modelContext.Tree, modelContext.SemanticModel)); } + }); - var treeMessages = c.Compilation.SyntaxTrees - .Where(x => ShouldGenerateMetrics(c, x)) - .Select(x => CreateMessage(x, c.Compilation.GetSemanticModel(x))); - var messages = CreateAnalysisMessages(c) + startContext.RegisterCompilationEndAction(endContext => + { + var allMessages = CreateAnalysisMessages(endContext) .Concat(treeMessages) .WhereNotNull() .ToArray(); @@ -106,12 +115,13 @@ protected sealed override void Initialize(SonarAnalysisContext context) => { Directory.CreateDirectory(OutPath); using var stream = File.Create(Path.Combine(OutPath, FileName)); - foreach (var message in messages) + foreach (var message in allMessages) { message.WriteDelimitedTo(stream); } } }); + }); protected virtual bool ShouldGenerateMetrics(SyntaxTree tree) => // The results of Metrics and CopyPasteToken analyzers are not needed for Test projects yet the plugin side expects the protobuf files, so we create empty ones. @@ -119,8 +129,8 @@ protected virtual bool ShouldGenerateMetrics(SyntaxTree tree) => && FileExtensionWhitelist.Contains(Path.GetExtension(tree.FilePath)) && (AnalyzeGeneratedCode || !Language.GeneratedCodeRecognizer.IsGenerated(tree)); - private bool ShouldGenerateMetrics(SonarCompilationReportingContext context, SyntaxTree tree) => - (AnalyzeUnchangedFiles || !context.IsUnchanged(tree)) - && ShouldGenerateMetrics(tree); + private bool ShouldGenerateMetrics(SonarSematicModelReportingContext context) => + (AnalyzeUnchangedFiles || !context.IsUnchanged(context.Tree)) + && ShouldGenerateMetrics(context.Tree); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarAnalysisContextTest.Register.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarAnalysisContextTest.Register.cs index 19640c54cfb..eb1261368fe 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarAnalysisContextTest.Register.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarAnalysisContextTest.Register.cs @@ -20,7 +20,6 @@ extern alias csharp; extern alias vbnet; - using Microsoft.CodeAnalysis.CSharp; using Moq; using SonarAnalyzer.AnalysisContext; @@ -132,6 +131,86 @@ public void RegisterCodeBlockStartAction_UnchangedFiles_SonarAnalysisContext(str context.AssertDelegateInvoked(expected); } + [TestMethod] + public void SonarCompilationStartAnalysisContext_RegisterCompilationEndAction() + { + var context = new DummyAnalysisContext(TestContext); + var startContext = new DummyCompilationStartAnalysisContext(context); + var sut = new SonarCompilationStartAnalysisContext(new(context, DummyMainDescriptor), startContext); + sut.RegisterCompilationEndAction(_ => { }); + + startContext.AssertExpectedInvocationCounts(expectedCompilationEndCount: 1); + } + + [TestMethod] + public void SonarCompilationStartAnalysisContext_RegisterSemanticModel() + { + var context = new DummyAnalysisContext(TestContext); + var startContext = new DummyCompilationStartAnalysisContext(context); + var sut = new SonarCompilationStartAnalysisContext(new(context, DummyMainDescriptor), startContext); + sut.RegisterSemanticModelAction(_ => { }); + + startContext.AssertExpectedInvocationCounts(expectedSemanticModelCount: 1); + } + + [TestMethod] + public void SonarCompilationStartAnalysisContext_RegisterSymbolAction() + { + var context = new DummyAnalysisContext(TestContext); + var startContext = new DummyCompilationStartAnalysisContext(context); + var sut = new SonarCompilationStartAnalysisContext(new(context, DummyMainDescriptor), startContext); + sut.RegisterSymbolAction(_ => { }); + + startContext.AssertExpectedInvocationCounts(expectedSymbolCount: 1); + } + + [TestMethod] + public void SonarCompilationStartAnalysisContext_RegisterNodeAction() + { + var context = new DummyAnalysisContext(TestContext); + var startContext = new DummyCompilationStartAnalysisContext(context); + var sut = new SonarCompilationStartAnalysisContext(new(context, DummyMainDescriptor), startContext); + sut.RegisterNodeAction(CSharpGeneratedCodeRecognizer.Instance, _ => { }); + + startContext.AssertExpectedInvocationCounts(expectedNodeCount: 0); // RegisterNodeAction doesn't use DummyCompilationStartAnalysisContext to register but a newly created context + } + + [TestMethod] + public void SonarCompilationStartAnalysisContext_RegisterSemanticModel_ReportsIssue() + { + var context = new DummyAnalysisContext(TestContext); + var startContext = new DummyCompilationStartAnalysisContext(context); + var sut = new SonarCompilationStartAnalysisContext(new(context, DummyMainDescriptor), startContext); + var diagnostic = Diagnostic.Create(DiagnosticDescriptorFactory.CreateUtility("TEST", "Test report"), context.Tree.GetRoot().GetLocation()); + sut.RegisterSemanticModelAction(x => x.ReportIssue(diagnostic)); + + startContext.RaisedDiagnostic.Should().NotBeNull().And.BeSameAs(diagnostic); + } + + [TestMethod] + public void SonarCompilationStartAnalysisContext_RegisterCompilationEnd_ReportsIssue() + { + var context = new DummyAnalysisContext(TestContext); + var startContext = new DummyCompilationStartAnalysisContext(context); + var sut = new SonarCompilationStartAnalysisContext(new(context, DummyMainDescriptor), startContext); + var diagnostic = Diagnostic.Create(DiagnosticDescriptorFactory.CreateUtility("TEST", "Test report"), context.Tree.GetRoot().GetLocation()); + sut.RegisterCompilationEndAction(x => x.ReportIssue(CSharpGeneratedCodeRecognizer.Instance, diagnostic)); + + startContext.RaisedDiagnostic.Should().NotBeNull().And.BeSameAs(diagnostic); + } + + [TestMethod] + public void SonarCompilationStartAnalysisContext_RegistSymbol_ReportsIssue() + { + var context = new DummyAnalysisContext(TestContext); + var startContext = new DummyCompilationStartAnalysisContext(context); + var sut = new SonarCompilationStartAnalysisContext(new(context, DummyMainDescriptor), startContext); + var diagnostic = Diagnostic.Create(DiagnosticDescriptorFactory.CreateUtility("TEST", "Test report"), context.Tree.GetRoot().GetLocation()); + sut.RegisterSymbolAction(x => x.ReportIssue(CSharpGeneratedCodeRecognizer.Instance, diagnostic)); + + startContext.RaisedDiagnostic.Should().NotBeNull().And.BeSameAs(diagnostic); + } + private static CompilationStartAnalysisContext MockCompilationStartAnalysisContext(DummyAnalysisContext context) { var mock = new Mock(context.Model.Compilation, context.Options, CancellationToken.None); @@ -200,6 +279,59 @@ public override void RegisterSyntaxNodeAction(Action throw new NotImplementedException(); } + private class DummyCompilationStartAnalysisContext : CompilationStartAnalysisContext + { + private readonly DummyAnalysisContext context; + private int compilationEndCount; + private int semanticModelCount; + private int symbolCount; + private int nodeCount; + + public Diagnostic RaisedDiagnostic { get; private set; } + + public DummyCompilationStartAnalysisContext(DummyAnalysisContext context) : base(context.Model.Compilation, context.Options, default) => + this.context = context; + + public void AssertExpectedInvocationCounts(int expectedCompilationEndCount = 0, int expectedSemanticModelCount = 0, int expectedSymbolCount = 0, int expectedNodeCount = 0) + { + compilationEndCount.Should().Be(expectedCompilationEndCount); + semanticModelCount.Should().Be(expectedSemanticModelCount); + symbolCount.Should().Be(expectedSymbolCount); + nodeCount.Should().Be(expectedNodeCount); + } + + public override void RegisterCodeBlockAction(Action action) => + throw new NotImplementedException(); + + public override void RegisterCodeBlockStartAction(Action> action) => + throw new NotImplementedException(); + + public override void RegisterCompilationEndAction(Action action) + { + compilationEndCount++; + action(new CompilationAnalysisContext(context.Model.Compilation, context.Options, reportDiagnostic: x => RaisedDiagnostic = x, isSupportedDiagnostic: _ => true, CancellationToken.None)); + } + + public override void RegisterSemanticModelAction(Action action) + { + semanticModelCount++; + action(new SemanticModelAnalysisContext(context.Model, context.Options, reportDiagnostic: x => RaisedDiagnostic = x, isSupportedDiagnostic: _ => true, CancellationToken.None)); + } + + public override void RegisterSymbolAction(Action action, ImmutableArray symbolKinds) + { + symbolCount++; + action(new SymbolAnalysisContext(Mock.Of(), context.Model.Compilation, context.Options, + reportDiagnostic: x => RaisedDiagnostic = x, isSupportedDiagnostic: _ => true, CancellationToken.None)); + } + + public override void RegisterSyntaxNodeAction(Action action, ImmutableArray syntaxKinds) => + nodeCount++; + + public override void RegisterSyntaxTreeAction(Action action) => + throw new NotImplementedException(); + } + [DiagnosticAnalyzer(LanguageNames.CSharp)] private class DummyAnalyzerForGenerated : SonarDiagnosticAnalyzer { diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockReportingContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockReportingContextTest.cs index ed2cb8835b4..cca5540acbb 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockReportingContextTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockReportingContextTest.cs @@ -38,12 +38,12 @@ public void Properties_ArePropagated() var context = new CodeBlockAnalysisContext(codeBlock, owningSymbol, model, options, _ => { }, _ => true, cancel); var sut = new SonarCodeBlockReportingContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context); - sut.Tree.Should().Be(codeBlock.SyntaxTree); - sut.Compilation.Should().Be(model.Compilation); - sut.Options.Should().Be(options); + sut.Tree.Should().BeSameAs(codeBlock.SyntaxTree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.Options.Should().BeSameAs(options); sut.Cancel.Should().Be(cancel); - sut.CodeBlock.Should().Be(codeBlock); - sut.OwningSymbol.Should().Be(owningSymbol); - sut.SemanticModel.Should().Be(model); + sut.CodeBlock.Should().BeSameAs(codeBlock); + sut.OwningSymbol.Should().BeSameAs(owningSymbol); + sut.SemanticModel.Should().BeSameAs(model); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockStartAnalysisContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockStartAnalysisContextTest.cs index 2ccfb0a4587..ec47ba9d376 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockStartAnalysisContextTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCodeBlockStartAnalysisContextTest.cs @@ -38,12 +38,12 @@ public void Properties_ArePropagated() var context = new Mock>(codeBlock, owningSymbol, model, options, cancel).Object; var sut = new SonarCodeBlockStartAnalysisContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context); - sut.Tree.Should().Be(codeBlock.SyntaxTree); - sut.Compilation.Should().Be(model.Compilation); - sut.Options.Should().Be(options); + sut.Tree.Should().BeSameAs(codeBlock.SyntaxTree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.Options.Should().BeSameAs(options); sut.Cancel.Should().Be(cancel); - sut.CodeBlock.Should().Be(codeBlock); - sut.OwningSymbol.Should().Be(owningSymbol); - sut.SemanticModel.Should().Be(model); + sut.CodeBlock.Should().BeSameAs(codeBlock); + sut.OwningSymbol.Should().BeSameAs(owningSymbol); + sut.SemanticModel.Should().BeSameAs(model); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationReportingContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationReportingContextTest.cs index e676760cdcd..cd500aba52f 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationReportingContextTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationReportingContextTest.cs @@ -34,9 +34,9 @@ public void Properties_ArePropagated() var context = new CompilationAnalysisContext(model.Compilation, options, _ => { }, _ => true, cancel); var sut = new SonarCompilationReportingContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context); - sut.Tree.Should().Be(tree); - sut.Compilation.Should().Be(model.Compilation); - sut.Options.Should().Be(options); + sut.Tree.Should().BeSameAs(tree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.Options.Should().BeSameAs(options); sut.Cancel.Should().Be(cancel); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationStartAnalysisContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationStartAnalysisContextTest.cs index fde4e28c0c7..a91a7f66909 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationStartAnalysisContextTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarCompilationStartAnalysisContextTest.cs @@ -35,9 +35,9 @@ public void Properties_ArePropagated() var context = new Mock(model.Compilation, options, cancel).Object; var sut = new SonarCompilationStartAnalysisContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context); - sut.Tree.Should().Be(tree); - sut.Compilation.Should().Be(model.Compilation); - sut.Options.Should().Be(options); + sut.Tree.Should().BeSameAs(tree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.Options.Should().BeSameAs(options); sut.Cancel.Should().Be(cancel); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSematicModelReportingContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSematicModelReportingContextTest.cs new file mode 100644 index 00000000000..a01a1ed8eb8 --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSematicModelReportingContextTest.cs @@ -0,0 +1,43 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2023 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarAnalyzer.AnalysisContext; + +namespace SonarAnalyzer.UnitTest.AnalysisContext; + +[TestClass] +public class SonarSematicModelReportingContextTest +{ + [TestMethod] + public void Properties_ArePropagated() + { + var cancel = new CancellationToken(true); + var (tree, model) = TestHelper.CompileCS("// Nothing to see here"); + var options = AnalysisScaffolding.CreateOptions(); + var context = new SemanticModelAnalysisContext(model, options, _ => { }, _ => true, cancel); + var sut = new SonarSematicModelReportingContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context); + + sut.Tree.Should().BeSameAs(tree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.SemanticModel.Should().BeSameAs(model); + sut.Options.Should().BeSameAs(options); + sut.Cancel.Should().Be(cancel); + } +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSymbolReportingContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSymbolReportingContextTest.cs index 69b18737bd6..5de2d2c8a0d 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSymbolReportingContextTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSymbolReportingContextTest.cs @@ -37,10 +37,10 @@ public void Properties_ArePropagated() var context = new SymbolAnalysisContext(symbol, model.Compilation, options, _ => { }, _ => true, cancel); var sut = new SonarSymbolReportingContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context); - sut.Tree.Should().Be(tree); - sut.Compilation.Should().Be(model.Compilation); - sut.Options.Should().Be(options); + sut.Tree.Should().BeSameAs(tree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.Options.Should().BeSameAs(options); sut.Cancel.Should().Be(cancel); - sut.Symbol.Should().Be(symbol); + sut.Symbol.Should().BeSameAs(symbol); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxNodeReportingContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxNodeReportingContextTest.cs index 56a84db92fd..46afbe39457 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxNodeReportingContextTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxNodeReportingContextTest.cs @@ -37,13 +37,13 @@ public void Properties_ArePropagated() var context = new SyntaxNodeAnalysisContext(node, containingSymbol, model, options, _ => { }, _ => true, cancel); var sut = new SonarSyntaxNodeReportingContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context); - sut.Tree.Should().Be(tree); - sut.Compilation.Should().Be(model.Compilation); - sut.Options.Should().Be(options); + sut.Tree.Should().BeSameAs(tree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.Options.Should().BeSameAs(options); sut.Cancel.Should().Be(cancel); - sut.Node.Should().Be(node); - sut.SemanticModel.Should().Be(model); - sut.ContainingSymbol.Should().Be(containingSymbol); + sut.Node.Should().BeSameAs(node); + sut.SemanticModel.Should().BeSameAs(model); + sut.ContainingSymbol.Should().BeSameAs(containingSymbol); } #if NET // .NET Fx shows the message box directly, the exception cannot be caught diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxTreeReportingContextTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxTreeReportingContextTest.cs index 86dfbaac437..1ceab7b3cb7 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxTreeReportingContextTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/AnalysisContext/SonarSyntaxTreeReportingContextTest.cs @@ -45,9 +45,9 @@ public void Properties_ArePropagated() var context = new SyntaxTreeAnalysisContext(tree, options, _ => { }, _ => true, cancel); var sut = new SonarSyntaxTreeReportingContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context, model.Compilation); - sut.Tree.Should().Be(tree); - sut.Compilation.Should().Be(model.Compilation); - sut.Options.Should().Be(options); + sut.Tree.Should().BeSameAs(tree); + sut.Compilation.Should().BeSameAs(model.Compilation); + sut.Options.Should().BeSameAs(options); sut.Cancel.Should().Be(cancel); } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/FileMetadataAnalyzerTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/FileMetadataAnalyzerTest.cs index dd796cbc97d..9f27d778f32 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/FileMetadataAnalyzerTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/FileMetadataAnalyzerTest.cs @@ -76,17 +76,15 @@ public void NotAutogenerated(ProjectType projectType) CreateBuilder(projectType, notAutogeneratedFiles) .WithSonarProjectConfigPath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, projectType)) .VerifyUtilityAnalyzer(messages => + messages.Should().BeEquivalentTo(notAutogeneratedFiles.Select(expected => new FileMetadataInfo { - messages[0].IsGenerated.Should().BeTrue(); - messages[0].FilePath.Should().Be("ExtraEmptyFile.g.cs"); - - for (var i = 0; i < notAutogeneratedFiles.Length; i++) - { - var message = messages[i + 1]; // The first message is for ExtraEmptyFile.g.cs, then is our list - message.IsGenerated.Should().BeFalse(); - message.FilePath.Should().Be(BasePath + notAutogeneratedFiles[i]); - } - }); + IsGenerated = false, + FilePath = BasePath + expected, + }).Append(new FileMetadataInfo + { + IsGenerated = true, + FilePath = "ExtraEmptyFile.g.cs" + }))); } [DataTestMethod] @@ -116,7 +114,7 @@ private void VerifyAllFilesAreGenerated(ProjectType projectType, string[] projec .VerifyUtilityAnalyzer(messages => { messages.Should().AllBeEquivalentTo(new { IsGenerated = true }); - messages.Should().Equal(autogeneratedFiles, equalityComparison: (m, a) => m.FilePath.EndsWith(a)); + messages.Should().SatisfyRespectively(autogeneratedFiles.Select>(expected => actual => actual.FilePath.EndsWith(expected))); }); private VerifierBuilder CreateBuilder(ProjectType projectType, params string[] projectFiles) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/UtilityAnalyzerBaseTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/UtilityAnalyzerBaseTest.cs index b6bc2cebaa2..8f03a14688f 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/UtilityAnalyzerBaseTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/Utilities/UtilityAnalyzerBaseTest.cs @@ -21,11 +21,11 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic; +using Moq; using SonarAnalyzer.AnalysisContext; using SonarAnalyzer.Common; +using SonarAnalyzer.Extensions; using SonarAnalyzer.Rules; -using SonarAnalyzer.UnitTest.AnalysisContext; -using SonarAnalyzer.UnitTest.Helpers; namespace SonarAnalyzer.UnitTest.Rules.Utilities { @@ -136,12 +136,12 @@ public TestUtilityAnalyzer(string language, params string[] additionalPaths) : b LanguageNames.VisualBasic => VisualBasicCompilation.Create(null), _ => throw new InvalidOperationException($"Unexpected {nameof(language)}: {language}") }; - var c = new CompilationAnalysisContext(compilation, new AnalyzerOptions(additionalFiles), null, null, default); - ReadParameters(new(AnalysisScaffolding.CreateSonarAnalysisContext(), c)); + var context = new Mock(compilation, new AnalyzerOptions(additionalFiles), default).Object; + ReadParameters(new SonarCompilationStartAnalysisContext(AnalysisScaffolding.CreateSonarAnalysisContext(), context)); } protected override void Initialize(SonarAnalysisContext context) => - throw new NotImplementedException(); + throw new NotSupportedException(); } } }