Skip to content

Commit

Permalink
fix: visual basic bugs (#198)
Browse files Browse the repository at this point in the history
* fix: vb issues

fix incorrect module block name in model
manually add mscorlib for address vb build errors and bad semantic model

* test: add and update tests

* fix: argumentlist being treated as invocation expression

* fix: handle property as type of invocation expression
  • Loading branch information
cslong authored Jun 30, 2022
1 parent 5b199a8 commit b9e8ba3
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 21 deletions.
21 changes: 16 additions & 5 deletions src/Analysis/Codelyzer.Analysis.Build/ProjectBuildHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ private async Task<Compilation> SetPrePortCompilation()

return null;
}

private bool CanSkipErrorsForVisualBasic()
{
// Compilation returns false build errors, it seems like we can work around this with
Expand All @@ -132,12 +132,23 @@ private bool CanSkipErrorsForVisualBasic()
Compilation.SyntaxTrees.Any() &&
Compilation.GetSemanticModel(Compilation.SyntaxTrees.First()) != null;
}

private async Task SetCompilation()
{
PrePortCompilation = await SetPrePortCompilation();
Compilation = await Project.GetCompilationAsync();

PrePortCompilation = await SetPrePortCompilation();

if (Project.Language == "Visual Basic")
{
var netFrameworkPath = AnalyzerResult.Properties
.FirstOrDefault(s => s.Key == "FrameworkPathOverride")
.Value;
if (!string.IsNullOrEmpty(netFrameworkPath))
{
Project = Project.AddMetadataReference(
MetadataReference.CreateFromFile(
$"{netFrameworkPath}\\mscorlib.dll"));
}
}
Compilation = await Project.GetCompilationAsync();
var errors = Compilation.GetDiagnostics()
.Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error);
if (errors.Any() && !CanSkipErrorsForVisualBasic())
Expand Down
51 changes: 49 additions & 2 deletions src/Analysis/Codelyzer.Analysis.Model/ArgumentList.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
namespace Codelyzer.Analysis.Model
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace Codelyzer.Analysis.Model
{
public class ArgumentList : InvocationExpression
public class ArgumentList : UstNode
{
[Obsolete(Constants.ObsoleteParameterMessage, Constants.DoNotThrowErrorOnUse)]
[JsonProperty("parameters", Order = 50)]
public List<Parameter> Parameters { get; set; }

[JsonProperty("arguments", Order = 51)]
public List<Argument> Arguments { get; set; }

[JsonProperty("semantic-properties", Order = 65)]
public List<string> SemanticProperties { get; set; }
public ArgumentList()
: base(IdConstants.ArgumentListName)
{
SemanticProperties = new List<string>();
#pragma warning disable CS0618 // Type or member is obsolete
Parameters = new List<Parameter>();
#pragma warning restore CS0618 // Type or member is obsolete
Arguments = new List<Argument>();
}
public override bool Equals(object obj)
{
if (obj is ArgumentList)
{
return Equals(obj as ArgumentList);
}
return false;
}

public bool Equals(ArgumentList compareNode)
{
return
compareNode != null &&
#pragma warning disable CS0618 // Type or member is obsolete
Parameters?.SequenceEqual(compareNode.Parameters) != false &&
#pragma warning restore CS0618 // Type or member is obsolete
Arguments?.SequenceEqual(compareNode.Arguments) != false &&
base.Equals(compareNode);

}
public override int GetHashCode()
{
return HashCode.Combine(
#pragma warning disable CS0618 // Type or member is obsolete
HashCode.Combine(Parameters, Arguments),
#pragma warning restore CS0618 // Type or member is obsolete
base.GetHashCode());
}
}
}
2 changes: 1 addition & 1 deletion src/Analysis/Codelyzer.Analysis.Model/ModuleBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class ModuleBlock : BaseMethodDeclaration
public string SemanticAssembly { get; set; }

public ModuleBlock()
: base(IdConstants.AccessorBlockName)
: base(IdConstants.ModuleBlockName)
{
Reference = new Reference();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using System.Linq;
using Microsoft.Build.Evaluation;
using Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE;

namespace Codelyzer.Analysis.VisualBasic.Handlers
{
Expand Down Expand Up @@ -61,11 +63,21 @@ private void SetMetaData(InvocationExpressionSyntax syntaxNode)

if (SemanticModel == null) return;

IMethodSymbol invokedSymbol = (IMethodSymbol)SemanticHelper.GetSemanticSymbol(syntaxNode, SemanticModel, OriginalSemanticModel);

var symbol = SemanticHelper.GetSemanticSymbol(syntaxNode, SemanticModel, OriginalSemanticModel);
if (symbol is IPropertySymbol)
{
HandlePropertySymbol((IPropertySymbol)symbol);
return;
}

var invokedSymbol = (IMethodSymbol)symbol;
if (invokedSymbol == null) return;

//Set semantic details
HandleMethodSymbol(invokedSymbol);
}

private void HandleMethodSymbol(IMethodSymbol invokedSymbol)
{
Model.MethodName = invokedSymbol.Name;
if (invokedSymbol.ContainingNamespace != null)
Model.SemanticNamespace = invokedSymbol.ContainingNamespace.ToString();
Expand All @@ -78,8 +90,7 @@ private void SetMetaData(InvocationExpressionSyntax syntaxNode)
if (invokedSymbol.ContainingType != null)
{
string classNameWithNamespace = invokedSymbol.ContainingType.ToString();
Model.SemanticClassType = Model.SemanticNamespace == null ? classNameWithNamespace :
SemanticHelper.GetSemanticClassType(classNameWithNamespace, Model.SemanticNamespace);
Model.SemanticClassType = Model.SemanticNamespace == null ? classNameWithNamespace : SemanticHelper.GetSemanticClassType(classNameWithNamespace, Model.SemanticNamespace);
}

string originalDefinition = "";
Expand All @@ -91,6 +102,7 @@ private void SetMetaData(InvocationExpressionSyntax syntaxNode)
{
originalDefinition = invokedSymbol.ContainingType.ToString();
}

Model.SemanticOriginalDefinition =
$"{originalDefinition}.{Model.MethodName}({string.Join(", ", invokedSymbol.Parameters.Select(p => p.Type))})";

Expand All @@ -107,7 +119,43 @@ private void SetMetaData(InvocationExpressionSyntax syntaxNode)

//Set method properties
SemanticHelper.AddMethodProperties(invokedSymbol, Model.SemanticProperties);
}

void HandlePropertySymbol(IPropertySymbol invokedSymbol)
{
//Set semantic details
Model.MethodName = invokedSymbol.Name;
if (invokedSymbol.ContainingNamespace != null)
Model.SemanticNamespace = invokedSymbol.ContainingNamespace.ToString();

Model.SemanticMethodSignature = invokedSymbol.ToString();

if (invokedSymbol.Type != null)
Model.SemanticReturnType = invokedSymbol.Type.Name;

if (invokedSymbol.ContainingType != null)
{
string classNameWithNamespace = invokedSymbol.ContainingType.ToString();
Model.SemanticClassType = Model.SemanticNamespace == null ? classNameWithNamespace :
SemanticHelper.GetSemanticClassType(classNameWithNamespace, Model.SemanticNamespace);
}

string originalDefinition = "";
if (invokedSymbol.ContainingType != null)
{
originalDefinition = invokedSymbol.ContainingType.ToString();
}
Model.SemanticOriginalDefinition =
$"{originalDefinition}.{Model.MethodName}({string.Join(", ", invokedSymbol.Parameters.Select(p => p.Type))})";


Model.Reference.Namespace = GetNamespace(invokedSymbol);
Model.Reference.Assembly = GetAssembly(invokedSymbol);
Model.Reference.Version = GetAssemblyVersion(invokedSymbol);
Model.Reference.AssemblySymbol = invokedSymbol.ContainingAssembly;

//Set method properties
SemanticHelper.AddPropertyProperties(invokedSymbol, Model.SemanticProperties);
}
}
}
12 changes: 12 additions & 0 deletions src/Analysis/Codelyzer.Analysis.VisualBasic/SemanticHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,18 @@ public static void AddMethodProperties(IMethodSymbol invokedSymbol, List<string>
if (invokedSymbol.IsReadOnly) properties.Add("readonly");
}

public static void AddPropertyProperties(IPropertySymbol invokedSymbol, List<string> properties)
{
properties.Add(invokedSymbol.DeclaredAccessibility.ToString());
if (invokedSymbol.IsOverride) properties.Add("override");
if (invokedSymbol.IsAbstract) properties.Add("abstract");
if (invokedSymbol.IsExtern) properties.Add("extern");
if (invokedSymbol.IsSealed) properties.Add("sealed");
if (invokedSymbol.IsStatic) properties.Add("static");
if (invokedSymbol.IsVirtual) properties.Add("virtual");
if (invokedSymbol.IsReadOnly) properties.Add("readonly");
}

public static string GetSemanticClassType(string classNameWithNamespace, string semanticNamespace)
{
Match match = Regex.Match(classNameWithNamespace, String.Format("{0}.(.*)", Regex.Escape(semanticNamespace)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.Logging.Abstractions;
using Codelyzer.Analysis.VisualBasic;
using System.Linq;
using Codelyzer.Analysis.Model;

namespace Codelyzer.Analysis.Languages.UnitTests
{
Expand Down Expand Up @@ -329,6 +330,7 @@ Module Module1

Assert.True(moduleNode.GetType() == typeof(Model.ModuleBlock));
Assert.True(moduleNode.Children[0].GetType() == typeof(Model.ModuleStatement));
Assert.True(moduleNode.type == IdConstants.ModuleBlockName);
}

[Fact]
Expand Down
19 changes: 11 additions & 8 deletions tst/Codelyzer.Analysis.Tests/AnalyzerRefacTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ public async Task VBTestAnalyzer_Builds_Projects_Successfully(string solutionFil
var allBuildErrors = results.SelectMany(r => r.ProjectBuildResult.BuildErrors);

CollectionAssert.IsNotEmpty(results);
//CollectionAssert.IsEmpty(allBuildErrors); know issue for vb build errors
CollectionAssert.IsEmpty(allBuildErrors);
}
private string CopySolutionFolderToTemp(string solutionName)
{
Expand Down Expand Up @@ -372,7 +372,7 @@ public async Task VBTestSampleWebApi()

//Project has 19 nuget references and 18 framework/dll references:
Assert.Contains(result.ProjectResult.ExternalReferences.NugetReferences.Count, new int[] { 17, 20 });
Assert.Contains(result.ProjectResult.ExternalReferences.SdkReferences.Count, new int[] { 17, 20 });
Assert.Contains(result.ProjectResult.ExternalReferences.SdkReferences.Count, new int[] { 18, 20 });

Assert.AreEqual(40, result.ProjectResult.SourceFiles.Count);

Expand Down Expand Up @@ -400,8 +400,8 @@ public async Task VBTestSampleWebApi()

Assert.AreEqual(0, blockStatements.Count);
Assert.AreEqual(1, classBlocks.Count);
Assert.AreEqual(33, expressionStatements.Count);
Assert.AreEqual(28, invocationExpressions.Count);
Assert.AreEqual(19, expressionStatements.Count);
Assert.AreEqual(14, invocationExpressions.Count);
Assert.AreEqual(4, literalExpressions.Count);
Assert.AreEqual(3, methodBlocks.Count);
Assert.AreEqual(6, returnStatements.Count);
Expand All @@ -413,6 +413,10 @@ public async Task VBTestSampleWebApi()
Assert.AreEqual(14, argumentLists.Count);
Assert.AreEqual(13, memberAccess.Count);

var ex = invocationExpressions.First(expression =>
expression.SemanticOriginalDefinition == "System.Web.Mvc.Controller.View(String)");
Assert.IsNotNull(ex);

var semanticMethodSignatures = methodBlocks.Select(m => m.SemanticSignature);
Assert.True(semanticMethodSignatures.Any(methodSignature => string.Compare(
"Public Public Function Index() As System.Web.Mvc.ActionResult",
Expand All @@ -429,7 +433,7 @@ public async Task VBTestSampleWebApi()
Assert.AreEqual("Public", helpControllerClass.Modifiers);

var helpPageSampleKey = result.ProjectResult.SourceFileResults.First(f => f.FilePath.EndsWith("HelpPageSampleKey.vb"));
Assert.AreEqual(52, helpPageSampleKey.AllInvocationExpressions().Count);
Assert.AreEqual(26, helpPageSampleKey.AllInvocationExpressions().Count);

var dllFiles = Directory.EnumerateFiles(Path.Combine(result.ProjectResult.ProjectRootPath, "bin"), "*.dll");
Assert.AreEqual(16, dllFiles.Count());
Expand Down Expand Up @@ -598,10 +602,9 @@ private void ValidateVBWebApiResult(AnalyzerResult result)
Assert.False(result.ProjectBuildResult.IsSyntaxAnalysis);

Assert.AreEqual(40, result.ProjectResult.SourceFiles.Count);

//Project has 23 nuget references and 22 framework/dll references:

Assert.AreEqual(17, result.ProjectResult.ExternalReferences.NugetReferences.Count);
Assert.AreEqual(17, result.ProjectResult.ExternalReferences.SdkReferences.Count);
Assert.AreEqual(18, result.ProjectResult.ExternalReferences.SdkReferences.Count);

var helpController = result.ProjectResult.SourceFileResults.Where(f => f.FilePath.EndsWith("HelpController.vb")).FirstOrDefault();
var homeController = result.ProjectResult.SourceFileResults.Where(f => f.FilePath.EndsWith("HomeController.vb")).FirstOrDefault();
Expand Down

0 comments on commit b9e8ba3

Please sign in to comment.