Skip to content

Commit

Permalink
Fixed exception on Argument.TryParseToValue (#10)
Browse files Browse the repository at this point in the history
GetFullyQualifiedName() doesn't work with ObjectCreationExpressionSyntax when it's part of another project.
As it was just to fill something in the 'Object' of the Value, I've changed it to ToFullString() instead.
Added extra test cases to test other projects. Changed the SolutionBuilderHelper to be able to create a solution with 2 projects.
  • Loading branch information
MichielOda authored Nov 21, 2023
1 parent cec9856 commit 1584f28
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Common/RoslynEditor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace SSkyline.DataMiner.CICD.CSharpAnalysis
namespace Skyline.DataMiner.CICD.CSharpAnalysis
{
using System;
using System.Collections.Generic;
Expand Down
5 changes: 1 addition & 4 deletions Common/RoslynHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
using Skyline.DataMiner.CICD.CSharpAnalysis.Classes;
using Skyline.DataMiner.CICD.CSharpAnalysis.Enums;

using SSkyline.DataMiner.CICD.CSharpAnalysis;

/// <summary>
/// Roslyn helper class.
/// </summary>
Expand Down Expand Up @@ -458,7 +456,6 @@ public static bool TryGetVariableAssignment(AssignmentExpressionSyntax expressio
return false;
}


/// <summary>
/// Tries parsing the specified expression as a value.
/// </summary>
Expand Down Expand Up @@ -850,7 +847,7 @@ public static bool TryParseValue(ExpressionSyntax expression, SemanticModel sema
{
Type = Value.ValueType.Unknown,
HasNotChanged = true,
Object = GetFullyQualifiedName(semanticModel, oces),
Object = oces.ToFullString(),
};
succeeded = true;
break;
Expand Down
79 changes: 74 additions & 5 deletions CommonTests/CheckCallingMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,22 @@ public void CheckCallingMethod_TryParseValue_ReferencedVariables()
{
List<string> results = new List<string>();

string precompileText = @"
namespace MyOtherNamespace
{
using System.Collections.Generic;
public static class MyOtherConstants
{
public static Queue<int> TriggerQueue = new Queue<int>();
public const double MyDoubleConst = 123.45;
}
}";

string programText = @"
namespace MyNamespace
{
using MyOtherNamespace;
//using Skyline.DataMiner.Scripting;
public static class MyConstants
Expand Down Expand Up @@ -106,6 +119,9 @@ public static void Run(int randomValue)
int d = 5;
d++;
Method15(d);
Method16(MyOtherConstants.TriggerQueue.Dequeue());
Method17(MyOtherConstants.MyDoubleConst);
}
private static void Method1(object o) { }
Expand All @@ -123,15 +139,23 @@ private static void Method12(object o) { }
private static void Method13(object o) { }
private static void Method14(object o) { }
private static void Method15(object o) { }
private static void Method16(object o) { }
private static void Method17(object o) { }
}
}";

var solution = SolutionBuilderHelper.CreateSolutionFromSource(programText);
var semanticModel = SolutionBuilderHelper.GetSemanticModel(solution);
var solution = SolutionBuilderHelper.CreateSolutionFromSource(programText, precompileText);

foreach (Project project in solution.Projects)
{
var semanticModel = SolutionBuilderHelper.BuildProject(project);

QActionAnalyzer analyzer = new QActionAnalyzer(semanticModel, CheckCallingMethod, solution);
RoslynVisitor parser = new RoslynVisitor(analyzer);
parser.Visit(semanticModel.SyntaxTree.GetRoot());

QActionAnalyzer analyzer = new QActionAnalyzer(semanticModel, CheckCallingMethod, solution);
RoslynVisitor parser = new RoslynVisitor(analyzer);
parser.Visit(semanticModel.SyntaxTree.GetRoot());
results.RemoveAll(String.IsNullOrWhiteSpace);
}

results.RemoveAll(String.IsNullOrWhiteSpace);
if (results.Count > 0)
Expand Down Expand Up @@ -163,6 +187,10 @@ void CheckCallingMethod(CallingMethodClass callingMethod, SemanticModel semantic
results.Add(Method14(callingMethod, semanticModel, solution));

results.Add(Method15(callingMethod, semanticModel, solution));

// Using type from another project
results.Add(Method16(callingMethod, semanticModel, solution));
results.Add(Method17(callingMethod, semanticModel, solution));
}

string Method1(CallingMethodClass callingMethod, SemanticModel semanticModel, Solution solution)
Expand Down Expand Up @@ -494,6 +522,47 @@ string Method15(CallingMethodClass callingMethod, SemanticModel semanticModel, S

return String.Empty;
}

string Method16(CallingMethodClass callingMethod, SemanticModel semanticModel, Solution solution)
{
if (callingMethod.Name != "Method16")
{
return null;
}

try
{
callingMethod.Arguments[0].TryParseToValue(semanticModel, solution, out Value value).Should().BeFalse();
}
catch (AssertFailedException e)
{
return $"[Method16] {e.Message}";
}

return String.Empty;
}

string Method17(CallingMethodClass callingMethod, SemanticModel semanticModel, Solution solution)
{
if (callingMethod.Name != "Method17")
{
return null;
}

try
{
callingMethod.Arguments[0].TryParseToValue(semanticModel, solution, out Value value).Should().BeTrue();
value.Object.Should().BeEquivalentTo(123.45);
value.Type.Should().Be(Value.ValueType.Double);
value.HasNotChanged.Should().BeTrue();
}
catch (AssertFailedException e)
{
return $"[Method17] {e.Message}";
}

return String.Empty;
}
}

private class QActionAnalyzer : CSharpAnalyzerBase
Expand Down
83 changes: 65 additions & 18 deletions CommonTests/SolutionBuilderHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ internal class SolutionBuilderHelper
{
public static Solution CreateSolutionFromSource(string sourceCode)
{
ICollection<MetadataReference> defaultReferences = new List<MetadataReference>();
defaultReferences.Add(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location));
ICollection<MetadataReference> defaultReferences = new List<MetadataReference>
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location)
};

var adhocWorkspace = new AdhocWorkspace();

Expand All @@ -22,28 +24,73 @@ public static Solution CreateSolutionFromSource(string sourceCode)

adhocWorkspace.AddSolution(solutionInfo);

ProjectId projectId = ProjectId.CreateNewId();
string projectName = "Project_1";
ProjectId projectId = PrepareProject(versionStamp, defaultReferences, projectName, out ProjectInfo projectInfo);

AddProjectToSolution(adhocWorkspace, projectInfo);

AddCSharpFileToProject(sourceCode, adhocWorkspace, projectId);
return adhocWorkspace.CurrentSolution;
}

public static Solution CreateSolutionFromSource(string sourceCode, string precompile)
{
ICollection<MetadataReference> defaultReferences = new List<MetadataReference>
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location)
};

var adhocWorkspace = new AdhocWorkspace();

VersionStamp versionStamp = VersionStamp.Create();

SolutionId solutionId = SolutionId.CreateNewId("solution");
SolutionInfo solutionInfo = SolutionInfo.Create(solutionId, versionStamp);

adhocWorkspace.AddSolution(solutionInfo);

string projectName = "Project_1";
CSharpParseOptions parseOptions = new CSharpParseOptions(LanguageVersion.CSharp7_3);
CompilationOptions compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
ProjectId projectId = PrepareProject(versionStamp, defaultReferences, projectName, out ProjectInfo projectInfo);

ProjectInfo projectInfo = ProjectInfo.Create(projectId, versionStamp, projectName, projectName, LanguageNames.CSharp)
.WithCompilationOptions(compilationOptions)
.WithParseOptions(parseOptions)
.WithMetadataReferences(defaultReferences);
projectName = "Project_Precompile";
ProjectId precompileProjectId = PrepareProject(versionStamp, defaultReferences, projectName, out ProjectInfo precompileProjectInfo);

var solution = adhocWorkspace.CurrentSolution;
solution = solution.AddProject(projectInfo);
bool result = adhocWorkspace.TryApplyChanges(solution);
solution = adhocWorkspace.CurrentSolution;
// Add link between project & precompile project
projectInfo = projectInfo.WithProjectReferences(new[] { new ProjectReference(precompileProjectId) });

AddProjectToSolution(adhocWorkspace, precompileProjectInfo);
AddProjectToSolution(adhocWorkspace, projectInfo);

AddCSharpFileToProject(precompile, adhocWorkspace, precompileProjectId);
AddCSharpFileToProject(sourceCode, adhocWorkspace, projectId);
return adhocWorkspace.CurrentSolution;
}

private static void AddCSharpFileToProject(string sourceCode, AdhocWorkspace adhocWorkspace, ProjectId projectId)
{
var sourceText = SourceText.From(sourceCode);
var doc = adhocWorkspace.AddDocument(projectId, "Class_1.cs", sourceText);
adhocWorkspace.AddDocument(projectId, "Class_1.cs", sourceText);
adhocWorkspace.TryApplyChanges(adhocWorkspace.CurrentSolution);
}

private static void AddProjectToSolution(AdhocWorkspace adhocWorkspace, ProjectInfo projectInfo)
{
var solution = adhocWorkspace.CurrentSolution.AddProject(projectInfo);
adhocWorkspace.TryApplyChanges(solution);
solution = adhocWorkspace.CurrentSolution;
}

private static ProjectId PrepareProject(VersionStamp versionStamp, ICollection<MetadataReference> defaultReferences, string projectName, out ProjectInfo projectInfo)
{
ProjectId projectId = ProjectId.CreateNewId();

CSharpParseOptions parseOptions = new CSharpParseOptions(LanguageVersion.CSharp7_3);
CompilationOptions compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);

return solution;
projectInfo = ProjectInfo.Create(projectId, versionStamp, projectName, projectName, LanguageNames.CSharp)
.WithCompilationOptions(compilationOptions)
.WithParseOptions(parseOptions)
.WithMetadataReferences(defaultReferences);
return projectId;
}

public static SemanticModel GetSemanticModel(Solution solution)
Expand All @@ -54,7 +101,7 @@ public static SemanticModel GetSemanticModel(Solution solution)
return BuildProject(project);
}

private static SemanticModel BuildProject(Project project)
public static SemanticModel BuildProject(Project project)
{
SemanticModel semanticModel = null;

Expand All @@ -72,7 +119,7 @@ private static SemanticModel BuildProject(Project project)
}
}
if(compilationErrors.Count > 0)
if (compilationErrors.Count > 0)
{
throw new ArgumentException("Could not compile project: " + String.Join(Environment.NewLine, compilationErrors));
}
Expand Down

0 comments on commit 1584f28

Please sign in to comment.