diff --git a/src/CTA.Rules.Actions/VisualBasic/AttributeListActions.cs b/src/CTA.Rules.Actions/VisualBasic/AttributeListActions.cs index 1a1f45d5..86f0d128 100644 --- a/src/CTA.Rules.Actions/VisualBasic/AttributeListActions.cs +++ b/src/CTA.Rules.Actions/VisualBasic/AttributeListActions.cs @@ -20,7 +20,7 @@ AttributeListSyntax AddComment(SyntaxGenerator syntaxGenerator, AttributeListSyn var currentTrivia = node.GetLeadingTrivia(); currentTrivia = currentTrivia.Insert(0, SyntaxFactory.SyntaxTrivia(SyntaxKind.CommentTrivia, - string.Format(Constants.CommentFormat, comment))); + string.Format(Constants.VbCommentFormat, comment))); node = node.WithLeadingTrivia(currentTrivia).NormalizeWhitespace(); return node; } diff --git a/src/CTA.Rules.Actions/VisualBasic/ObjectCreationExpressionActions.cs b/src/CTA.Rules.Actions/VisualBasic/ObjectCreationExpressionActions.cs index 46560162..b5e878d5 100644 --- a/src/CTA.Rules.Actions/VisualBasic/ObjectCreationExpressionActions.cs +++ b/src/CTA.Rules.Actions/VisualBasic/ObjectCreationExpressionActions.cs @@ -104,7 +104,7 @@ public Func assemblyPaths) var types = assembly.GetTypes() .Where(t => t.Name.EndsWith("Actions") && (t.Namespace.EndsWith(ProjectLanguage.VisualBasic.ToString()) || - t.Name.StartsWith("Project"))).ToList(); + t.Name.StartsWith("Project") || + t.Name.StartsWith("MemberAccess"))).ToList(); TryCreateInstance(Constants.CompilationUnitActions, types, out _compilationUnitObject); TryCreateInstance(Constants.InvocationExpressionActions, types, out _invocationExpressionObject); TryCreateInstance(Constants.NamespaceActions, types, out _namespaceObject); diff --git a/src/CTA.Rules.Update/VisualBasicActionsRewriter.cs b/src/CTA.Rules.Update/VisualBasicActionsRewriter.cs index 4112aa3a..d3693e1f 100644 --- a/src/CTA.Rules.Update/VisualBasicActionsRewriter.cs +++ b/src/CTA.Rules.Update/VisualBasicActionsRewriter.cs @@ -268,11 +268,16 @@ public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax no return newNode; } - var nodeKey = symbol.OriginalDefinition.ToString(); + var methodSymbol = (IMethodSymbol)symbol; + var prefix = methodSymbol.IsExtensionMethod + ? methodSymbol.ReceiverType?.ToString() ?? "" + : methodSymbol.ContainingType?.ToString() ?? ""; + var nodeKey = + $"{prefix}.{symbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Type))})"; foreach (var action in _allActions.OfType()) { - if (nodeKey == action.Key) + if (string.Equals(nodeKey, action.Key, StringComparison.OrdinalIgnoreCase)) { var actionExecution = new GenericActionExecution(action, _filePath) { TimesRun = 1 }; try @@ -325,7 +330,7 @@ public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax foreach (var action in _allActions.OfType>()) { - if (nodeKey == action.Key) + if (string.Equals(nodeKey,action.Key, StringComparison.OrdinalIgnoreCase)) { var actionExecution = new GenericActionExecution(action, _filePath) { TimesRun = 1 }; try diff --git a/tst/CTA.Rules.Test/Actions/VisualBasic/ActionsLoaderTests.cs b/tst/CTA.Rules.Test/Actions/VisualBasic/ActionsLoaderTests.cs index 9df676f8..b6d8c1c4 100644 --- a/tst/CTA.Rules.Test/Actions/VisualBasic/ActionsLoaderTests.cs +++ b/tst/CTA.Rules.Test/Actions/VisualBasic/ActionsLoaderTests.cs @@ -243,5 +243,15 @@ public void ProjectLevelActionsTest() Assert.IsNotNull(migrateConfig); Assert.IsNotNull(createMonolithService); } + + [Test] + public void MemberAccessExpressions() + { + var getRemoveMemberAccessAction = _actionLoader.GetMemberAccessExpressionActions("RemoveMemberAccess", ""); + var addComment = _actionLoader.GetMemberAccessExpressionActions("AddComment", "comment"); + + Assert.IsNotNull(getRemoveMemberAccessAction); + Assert.IsNotNull(addComment); + } } } \ No newline at end of file diff --git a/tst/CTA.Rules.Test/VisualBasicActionRewriterTest.cs b/tst/CTA.Rules.Test/VisualBasicActionRewriterTest.cs new file mode 100644 index 00000000..d55b9881 --- /dev/null +++ b/tst/CTA.Rules.Test/VisualBasicActionRewriterTest.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CTA.Rules.Actions; +using CTA.Rules.Models; +using CTA.Rules.Models.Actions.VisualBasic; +using CTA.Rules.Update; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.Syntax; +using NUnit.Framework; +using AttributeAction = CTA.Rules.Models.Actions.VisualBasic.AttributeAction; +using GenericAction = CTA.Rules.Models.GenericAction; +using ObjectCreationExpressionAction = CTA.Rules.Models.Actions.VisualBasic.ObjectCreationExpressionAction; + +namespace CTA.Rules.Test; +public class VisualBasicActionRewriterTest +{ + private string codeBlock = @" +Imports System + +Public Namespace TestNamespace + Public Module TestModule + Public Sub Main() + Math.Abs(1) + dim test = New TestClass() + End Sub + End Module + + + Public Class TestClass + End Class + + Public Interface TestInterface + End Interface +End Namespace +"; + + private VisualBasicSyntaxTree _syntaxTree; + private SemanticModel _semanticModel; + private VisualBasicCompilation _compilation; + private AdhocWorkspace _workspace; + private VisualBasicActionsLoader _actionsLoader; + + [SetUp] + public void Setup() + { + _syntaxTree = (VisualBasicSyntaxTree) VisualBasicSyntaxTree.ParseText(codeBlock); + var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); + _compilation = VisualBasicCompilation.Create("MyCompilation", + syntaxTrees: new[] {_syntaxTree}, references: new[] {mscorlib}); + _semanticModel = _compilation.GetSemanticModel(_syntaxTree); + _workspace = new AdhocWorkspace(); + _actionsLoader = new VisualBasicActionsLoader(new List()); + + var diagnostics = _compilation.GetDiagnostics(); + Console.WriteLine(1); + } + + [Test] + public void TestSingleActionRewriterOnAttributeList() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.AttributeList)); + var action = new AttributeAction + { + AttributeListActionFunc = _actionsLoader.GetAttributeListAction("AddComment", "Comment"), + Key = "Serializable" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + action); + var newNode = rewriter.VisitAttributeList((AttributeListSyntax)node); + Assert.IsTrue(newNode.GetLeadingTrivia().ToString().Contains("Comment")); + } + + [Test] + public void TestSingleNullActionOnAttributeList() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.AttributeList)); + var action = new AttributeAction + { + AttributeListActionFunc = null, + Key = "Serializable" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + action); + var newNode = rewriter.VisitAttributeList((AttributeListSyntax) node); + Assert.IsTrue(newNode == node); + } + + [Test] + public void TestVisitAttribute() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.Attribute)); + var action = new AttributeAction + { + AttributeActionFunc = _actionsLoader.GetAttributeAction("ChangeAttribute", "NewAttribute"), + Key = "Serializable" + }; + var nullAction = new AttributeAction() + { + AttributeActionFunc = null, + Key = "Serializable" + }; + + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitAttribute((AttributeSyntax)node); + Assert.IsTrue(newNode.ToString().Contains("NewAttribute")); + } + + [Test] + public void TestVisitInterfaceBlock() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.InterfaceBlock)); + var action = new InterfaceBlockAction + { + InterfaceBlockActionFunc = _actionsLoader.GetInterfaceAction("ChangeName", "NewInterfaceName"), + Key = "TestInterface" + }; + var nullAction = new InterfaceBlockAction + { + InterfaceBlockActionFunc = null, + Key = "TestInterface" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitInterfaceBlock((InterfaceBlockSyntax)node); + Assert.IsTrue(newNode.ToString().Contains("NewInterfaceName")); + } + + [Test] + public void TestVisitExpressionStatement() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.ExpressionStatement)); + var action = new ExpressionAction + { + ExpressionActionFunc = _actionsLoader.GetExpressionAction("AddComment", "NewComment"), + Key = "System.Math.Abs(integer)" + }; + var nullAction = new ExpressionAction + { + ExpressionActionFunc = null, + Key = "System.Math.Abs(integer)" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitExpressionStatement((ExpressionStatementSyntax)node); + Assert.IsTrue(newNode.GetLeadingTrivia().ToString().Contains("NewComment")); + } + + [Test] + public void TestVisitNamespaceBlock() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.NamespaceBlock)); + var action = new NamespaceAction() + { + NamespaceActionFunc = _actionsLoader.GetNamespaceActions("RenameNamespace", "NewNamespace"), + Key = "TestNamespace" + }; + var nullAction = new NamespaceAction() + { + NamespaceActionFunc = null, + Key = "TestNamespace" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitNamespaceBlock((NamespaceBlockSyntax)node); + Assert.IsTrue(newNode.ToString().Contains("NewNamespace")); + } + + [Test] + public void TestVisitClassBlock() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.ClassBlock)); + var action = new TypeBlockAction() + { + TypeBlockActionFunc = _actionsLoader.GetClassAction("ChangeName", "NewClass"), + Key = "TestClass" + }; + var nullAction = new TypeBlockAction() + { + TypeBlockActionFunc = null, + Key = "TestClass" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitClassBlock((ClassBlockSyntax)node); + Assert.IsTrue(newNode.ToString().Contains("NewClass")); + } + + [Test] + public void TestVisitModuleBlock() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.ModuleBlock)); + var action = new TypeBlockAction + { + TypeBlockActionFunc = _actionsLoader.GetClassAction("ChangeName", "NewModule"), + Key = "TestModule" + }; + var nullAction = new TypeBlockAction + { + TypeBlockActionFunc = null, + Key = "TestModule" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitModuleBlock((ModuleBlockSyntax)node); + Assert.IsTrue(newNode.ToString().Contains("NewModule")); + } + + [Test] + public void TestVisitMemberAccessExpression() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.SimpleMemberAccessExpression)); + var action = new MemberAccessAction() + { + MemberAccessActionFunc = _actionsLoader.GetMemberAccessExpressionActions("AddComment", "NewComment"), + Key = "System.Math.Abs" + }; + var nullAction = new MemberAccessAction() + { + MemberAccessActionFunc = null, + Key = "System.Math.Abs" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitMemberAccessExpression((MemberAccessExpressionSyntax)node); + Assert.IsTrue(newNode.GetLeadingTrivia().ToString().Contains("NewComment")); + } + + [Test] + public void TestVisitObjectCreationExpression() + { + var node = _syntaxTree.GetRoot() + .DescendantNodes() + .First(n => n.IsKind(SyntaxKind.ObjectCreationExpression)); + var action = new ObjectCreationExpressionAction() + { + ObjectCreationExpressionGenericActionFunc = _actionsLoader.GetObjectCreationExpressionActions("AddComment", "NewComment"), + Key = "New TestClass()" + }; + var nullAction = new ObjectCreationExpressionAction() + { + ObjectCreationExpressionGenericActionFunc = null, + Key = "New TestClass()" + }; + var rewriter = new VisualBasicActionsRewriter(_semanticModel, null, + SyntaxGenerator.GetGenerator(_workspace, "Visual Basic"), "", + new List{ action, nullAction }); + var newNode = rewriter.VisitObjectCreationExpression((ObjectCreationExpressionSyntax)node); + Assert.IsTrue(newNode.GetLeadingTrivia().ToString().Contains("NewComment")); + } +} \ No newline at end of file diff --git a/tst/CTA.Rules.Test/VisualBasicTests.cs b/tst/CTA.Rules.Test/VisualBasicTests.cs index 1285ac20..be6e271f 100644 --- a/tst/CTA.Rules.Test/VisualBasicTests.cs +++ b/tst/CTA.Rules.Test/VisualBasicTests.cs @@ -1,7 +1,14 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using Codelyzer.Analysis; +using Codelyzer.Analysis.Analyzer; +using CTA.Rules.Models; +using CTA.Rules.PortCore; +using Microsoft.Extensions.Logging.Abstractions; +using System.Threading.Tasks; using NUnit.Framework; namespace CTA.Rules.Test @@ -11,7 +18,7 @@ internal class VisualBasicTests : AwsRulesBaseTest private string _tempDir; private string _downloadLocation; private List _ctaFiles; - private readonly string _version = "net5.0"; + private readonly string _version = "net6.0"; //We don't care about version for CTA-only rules: [SetUp] @@ -51,9 +58,9 @@ public void TestOwinParadiseVb() //Check method actions StringAssert.Contains("UseEndpoints", signalR); - + //Check project porting - StringAssert.Contains("net5.0", projectFile); + StringAssert.Contains("net6.0", projectFile); StringAssert.Contains("Microsoft.AspNetCore.Diagnostics", projectFile); } @@ -66,13 +73,12 @@ public void TestVbNetMvc() _version) .ProjectResults.FirstOrDefault(); // Check that nothing is ported. - - // uncomment once template in datastore is merged. - // StringAssert.Contains( - // "v4.7.2", - // results.CsProjectContent); + StringAssert.Contains( + "v4.7.2", + results.CsProjectContent); } - + + [Test] public void TestMixedClassLibrary() { @@ -84,10 +90,72 @@ public void TestMixedClassLibrary() Assert.IsTrue(projresults != null); Assert.IsTrue(projresults.Count() == 2); //check both projects ported - Assert.IsTrue(projresults.All(content => content.Contains("net5.0"))); + Assert.IsTrue(projresults.All(content => content.Contains("net6.0"))); Assert.IsTrue(slnResults.ProjectResults .Find(p => p.CsProjectPath.EndsWith(".vbproj")) .CsProjectContent.Contains("BouncyCastle.NetCore")); } + + [Test] + public async Task RunMixedUsingGenerator() + { + var solutionPath = CopySolutionFolderToTemp("MixedClassLibrary.sln", _tempDir); + + AnalyzerConfiguration configuration = new AnalyzerConfiguration(LanguageOptions.CSharp) + { + ExportSettings = + { + GenerateJsonOutput = false, + OutputPath = @"/tmp/UnitTests" + }, + + MetaDataSettings = + { + LiteralExpressions = true, + MethodInvocations = true, + Annotations = true, + DeclarationNodes = true, + LocationData = false, + ReferenceData = true, + LoadBuildData = true, + ElementAccess = true, + MemberAccess = true + } + }; + + //CodeAnalyzer analyzer = CodeAnalyzerFactory.GetAnalyzer(configuration, NullLogger.Instance); + CodeAnalyzerByLanguage analyzer = new CodeAnalyzerByLanguage(configuration, NullLogger.Instance); + + SolutionPort solutionPort = new SolutionPort(solutionPath); + + var resultEnumerator = analyzer.AnalyzeSolutionGeneratorAsync(solutionPath).GetAsyncEnumerator(); + + while (await resultEnumerator.MoveNextAsync()) + { + using var result = resultEnumerator.Current; + PortCoreConfiguration projectConfiguration = new PortCoreConfiguration() + { + SolutionPath = solutionPath, + ProjectPath = result.ProjectResult.ProjectFilePath, + IsMockRun = false, + UseDefaultRules = true, + PortCode = true, + PortProject = true, + TargetVersions = new List { _version } + }; + + solutionPort.RunProject(result, projectConfiguration); + } + var portSolutionResult = solutionPort.GenerateResults(); + var testSolutionResult = GenerateSolutionResult(solutionPath, solutionPort.GetAnalysisResult(), portSolutionResult); + var projResults = testSolutionResult.ProjectResults.Select(p => p.CsProjectContent).ToList(); + Assert.IsTrue(projResults != null); + Assert.IsTrue(projResults.Count() == 2); + //check both projects ported + Assert.IsTrue(projResults.All(content => content.Contains("net6.0"))); + Assert.IsTrue(testSolutionResult.ProjectResults + .Find(p => p.CsProjectPath.EndsWith(".vbproj")) + .CsProjectContent.Contains("BouncyCastle.NetCore")); + } } }