0

I have made a Visual Studio extension that can be used within Visual Studio 2022 to upload certain types of files (.cs and .vb) to a code security scanning engine. To do this, I’m using a tool called Roslyn to check that the code in these files is written correctly. If the code passes this check, it will be sent to the scanning engine for further analysis.

Recently, some new features were added to .NET 6 and 7. However, when I tried to use these features with my Visual Studio extension, I found that some errors were not being caught by the regular checking process within Visual Studio. These errors were instead being detected by the Code Analysis tools provided by Roslyn.

One example of this was when I used a top-level code template in .NET 6. Even though Visual Studio 2022 did not detect any issues with the code, the Code Analysis tools in Roslyn were able to identify problems with the use of top-level statements that did not include using statements.

To fix this issue, I updated the Code Analysis packages that my extension was using.

  1. Microsoft.CodeAnalysis (from version 1.0.0 to versions 4.0.1, 4.1.0, 4.2.0, and the latest stable version)
  2. Microsoft.CodeAnalysis.Analyzers (from version 3.3.2 to version 3.3.4)
  3. Microsoft.CodeAnalysis.Common (from version 1.0.0 to versions 4.0.1, 4.1.0, 4.2.0, and the latest stable version)
  4. Microsoft.CodeAnalysis.CSharp (from version 1.0.0 to versions 4.0.1, 4.1.0, 4.2.0, and the latest stable version)
  5. Microsoft.CodeAnalysis.CSharp.Workspaces (from version 1.0.0 to versions 4.0.1, 4.1.0, 4.2.0, and the latest stable version)
  6. Microsoft.CodeAnalysis.Scripting.Common (from version 1.0.0 to versions 4.0.1, 4.1.0, 4.2.0, and the latest stable version)
  7. Microsoft.CodeAnalysis.VisualBasic (from version 1.0.0 to versions 4.0.1, 4.1.0, 4.2.0, and the latest stable version)
  8. Microsoft.VisualStudio.LanguageServices (from version 1.0.0 to versions 4.0.1, 4.1.0, 4.2.0, and the latest stable version)

Despite updating the Code Analysis packages that my tool is using, the error that was previously being detected by the Code Analysis tools in Roslyn is still appearing even after compiling with the updated packages. I have also attempted to manually run the latest version of the Roslyn compiler, but the same error persists.

Reproduceable sample is as below

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.VisualBasic;

public static class Build
{
    private static async Task Emit(Workspace workspace, string outputFolderPath, string projectFilePath, string[] selectedFilePath)
    {
        await System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            Directory.CreateDirectory(outputFolderPath);

            var project = workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath.Equals(projectFilePath, StringComparison.CurrentCultureIgnoreCase));

            dynamic compilationOptions = Convert.ChangeType(project.CompilationOptions, project.CompilationOptions.GetType());

            compilationOptions = compilationOptions.WithOptimizationLevel(OptimizationLevel.Debug);
            compilationOptions = compilationOptions.WithMainTypeName(null);
            compilationOptions = compilationOptions.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
            compilationOptions = compilationOptions.WithPlatform(Platform.AnyCpu);
            project = project.WithCompilationOptions(compilationOptions);

            var sementicModel = project.Documents.Select(a => a.GetSemanticModelAsync().Result);
            var projectGraph = workspace.CurrentSolution.GetProjectDependencyGraph();
            var depedencies = projectGraph.GetProjectsThatThisProjectDirectlyDependsOn(project.Id).ToList();
            var refs = new List<MetadataReference>();

            foreach (ProjectId projectId in depedencies)
            {
                var depProject = workspace.CurrentSolution.Projects.Where(z => z.Id == projectId).FirstOrDefault();

                var depProjCompilation = depProject.GetCompilationAsync().Result;
                using (var projPeStream = new MemoryStream())
                {
                    var depCompilationResult = depProjCompilation.Emit(projPeStream);
                    if (depCompilationResult.Success)
                    {
                        projPeStream.Seek(0, SeekOrigin.Begin);
                        var portableReference = MetadataReference.CreateFromStream(projPeStream, filePath: depProject.Name);
                        refs.Add(portableReference);
                    }
                }
            }
            AnalyzeModel(sementicModel, project, outputFolderPath, selectedFilePath, refs);
        });
    }

    private static bool AnalyzeModel(IEnumerable<SemanticModel> sementinModels, Project project, string outputFolder, string[] filePaths, List<MetadataReference> refs)
    {
        try
        {
            var compilation = sementinModels.Select(a => a.Compilation).First();

            foreach (var tree in compilation.SyntaxTrees)
            {
                try
                {
                    compilation = compilation.RemoveSyntaxTrees(tree);
                }
                catch (Exception)
                {

                }
            }

            foreach (var file in filePaths)
            {
                try
                {
                    SemanticModel sModel = sementinModels.Where(t => t.SyntaxTree.FilePath == file).FirstOrDefault(); ;

                    var sModelSyntaxTree = sModel.SyntaxTree;
                    var selectedFilePath = sModelSyntaxTree.FilePath;
                    string selectedName = Path.GetFileName(selectedFilePath);

                    compilation = AddSyntaxTrees(compilation, new SyntaxTree[] { sModelSyntaxTree });
                }
                catch (Exception ex)
                {

                }
            }

            var metadataReferences = compilation.References.ToList();
            var metadataRefs = new List<MetadataReference>();

            foreach (var metadata in refs)
            {
                if (metadataReferences.Any(a => a.Display != metadata.Display))
                {
                    metadataRefs.Add(metadata);
                }
            }

            try
            {
                compilation = compilation.AddReferences(metadataRefs);
            }
            catch (Exception ex)
            {

            }

            var outFileName = Path.ChangeExtension("UploadAssembly", "dll");
            var pdbFileName = Path.ChangeExtension("UploadAssembly", "pdb");
            var emitResult = compilation.Emit(Path.Combine(outputFolder, outFileName), pdbPath: Path.Combine(outputFolder, pdbFileName));
            return emitResult.Success;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    private static Compilation AddSyntaxTrees(Compilation compilation, SyntaxTree[] syntaxTrees)
    {
        foreach (var syntaxTree in syntaxTrees)
        {
            if (!compilation.SyntaxTrees.Contains(syntaxTree))
                compilation = compilation.AddSyntaxTrees(new SyntaxTree[] { syntaxTree });
        }
        return compilation;
    }
}

It is giving false for emitResult.Success where expectation is to receive true

 var emitResult = compilation.Emit(Path.Combine(outputFolder, outFileName), pdbPath: Path.Combine(outputFolder, pdbFileName));
        return emitResult.Success;

Appreciate if you could shed some light here

enter image description here

2
  • 1
    A full minimal reproducible example would be great Commented Apr 7, 2023 at 20:13
  • Hi @GuruStron I have added sample to the post as requested. Could you kindly review and share your thoughts Commented Apr 10, 2023 at 11:46

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.