示例#1
0
        public AnalyzerConfigOptionsResult?GetAnalyzerConfigOptions()
        {
            // We need to find the analyzer config options at the root of the project.
            // Currently, there is no compiler API to query analyzer config options for a directory in a language agnostic fashion.
            // So, we use a dummy language-specific file name appended to the project directory to query analyzer config options.

            var projectDirectory = PathUtilities.GetDirectoryName(_projectInfo.FilePath);

            if (!PathUtilities.IsAbsolute(projectDirectory))
            {
                return(null);
            }

            var    fileName = Guid.NewGuid().ToString();
            string sourceFilePath;

            switch (_projectInfo.Language)
            {
            case LanguageNames.CSharp:
                // Suppression should be removed or addressed https://github.com/dotnet/roslyn/issues/41636
                sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.cs") !;
                break;

            case LanguageNames.VisualBasic:
                // Suppression should be removed or addressed https://github.com/dotnet/roslyn/issues/41636
                sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.vb") !;
                break;

            default:
                return(null);
            }

            return(_lazyAnalyzerConfigSet.GetValue(CancellationToken.None).GetOptionsForSourcePath(sourceFilePath));
        }
        /// <summary>
        /// Read the references from the project.lock.json file.
        /// </summary>
        internal static ImmutableArray <string> ReadProjectLockJson(string packagesDirectory, TextReader reader)
        {
            JObject obj;

            using (var jsonReader = new JsonTextReader(reader))
            {
                obj = JObject.Load(jsonReader);
            }
            var builder = ArrayBuilder <string> .GetInstance();

            var targets = (JObject)GetPropertyValue(obj, "targets");

            foreach (var target in targets)
            {
                if (target.Key == ProjectLockJsonFramework)
                {
                    foreach (var package in (JObject)target.Value)
                    {
                        var packageRoot = PathUtilities.CombineAbsoluteAndRelativePaths(packagesDirectory, package.Key);
                        var runtime     = (JObject)GetPropertyValue((JObject)package.Value, "runtime");
                        if (runtime == null)
                        {
                            continue;
                        }
                        foreach (var item in runtime)
                        {
                            var path = PathUtilities.CombinePossiblyRelativeAndRelativePaths(packageRoot, item.Key);
                            builder.Add(path);
                        }
                    }
                    break;
                }
            }
            return(builder.ToImmutableAndFree());
        }
        /// <exception cref="IOException"/>
        /// <exception cref="BadImageFormatException" />
        private AssemblyMetadata CreateAssemblyMetadata(
            FileKey fileKey, ModuleMetadata manifestModule, List <ITemporaryStreamStorage> storages,
            Func <FileKey, List <ITemporaryStreamStorage>, ModuleMetadata> moduleMetadataFactory)
        {
            ImmutableArray <ModuleMetadata> .Builder moduleBuilder = null;

            string assemblyDir = null;

            foreach (string moduleName in manifestModule.GetModuleNames())
            {
                if (moduleBuilder == null)
                {
                    moduleBuilder = ImmutableArray.CreateBuilder <ModuleMetadata>();
                    moduleBuilder.Add(manifestModule);
                    assemblyDir = Path.GetDirectoryName(fileKey.FullPath);
                }

                var moduleFileKey = FileKey.Create(PathUtilities.CombineAbsoluteAndRelativePaths(assemblyDir, moduleName));
                var metadata      = moduleMetadataFactory(moduleFileKey, storages);

                moduleBuilder.Add(metadata);
            }

            var modules = (moduleBuilder != null) ? moduleBuilder.ToImmutable() : ImmutableArray.Create(manifestModule);

            return(AssemblyMetadata.Create(modules));
        }
示例#4
0
        public ImmutableDictionary <string, ReportDiagnostic> GetAnalyzerConfigSpecialDiagnosticOptions()
        {
            // We need to find the analyzer config options at the root of the project.
            // Currently, there is no compiler API to query analyzer config options for a directory in a language agnostic fashion.
            // So, we use a dummy language-specific file name appended to the project directory to query analyzer config options.

            var projectDirectory = PathUtilities.GetDirectoryName(_projectInfo.FilePath);

            if (!PathUtilities.IsAbsolute(projectDirectory))
            {
                return(ImmutableDictionary <string, ReportDiagnostic> .Empty);
            }

            var    fileName = Guid.NewGuid().ToString();
            string sourceFilePath;

            switch (_projectInfo.Language)
            {
            case LanguageNames.CSharp:
                sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.cs");
                break;

            case LanguageNames.VisualBasic:
                sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.vb");
                break;

            default:
                return(ImmutableDictionary <string, ReportDiagnostic> .Empty);
            }

            return(_lazyAnalyzerConfigSet.GetValue(CancellationToken.None).GetOptionsForSourcePath(sourceFilePath).TreeOptions);
        }
        internal static string?ResolveStrongNameKeyFile(string path, StrongNameFileSystem fileSystem, ImmutableArray <string> keyFileSearchPaths)
        {
            // Dev11: key path is simply appended to the search paths, even if it starts with the current (parent) directory ("." or "..").
            // This is different from PathUtilities.ResolveRelativePath.

            if (PathUtilities.IsAbsolute(path))
            {
                if (fileSystem.FileExists(path))
                {
                    return(FileUtilities.TryNormalizeAbsolutePath(path));
                }

                return(path);
            }

            foreach (var searchPath in keyFileSearchPaths)
            {
                string?combinedPath = PathUtilities.CombineAbsoluteAndRelativePaths(searchPath, path);

                Debug.Assert(combinedPath == null || PathUtilities.IsAbsolute(combinedPath));

                if (fileSystem.FileExists(combinedPath))
                {
                    return(FileUtilities.TryNormalizeAbsolutePath(combinedPath !));
                }
            }

            return(null);
        }
示例#6
0
            /// <summary>
            /// Determines if the actual file path matches its logical path in project
            /// which is constructed as [project_root_path]\Logical\Folders\. The refactoring
            /// is triggered only when the two match. The reason of doing this is we don't really know
            /// the user's intention of keeping the file path out-of-sync with its logical path.
            /// </summary>
            private static bool IsDocumentPathRootedInProjectFolder(Document document)
            {
                var projectRoot = PathUtilities.GetDirectoryName(document.Project.FilePath);
                var folderPath  = Path.Combine(document.Folders.ToArray());

                var absoluteDircetoryPath = PathUtilities.GetDirectoryName(document.FilePath);
                var logicalDirectoryPath  = PathUtilities.CombineAbsoluteAndRelativePaths(projectRoot, folderPath);

                return(PathUtilities.PathsEqual(absoluteDircetoryPath, logicalDirectoryPath));
            }
示例#7
0
 protected (string folder, string filePath) CreateDocumentFilePath(string[] folder, string fileName = "DocumentA.cs")
 {
     if (folder == null || folder.Length == 0)
     {
         return(string.Empty, PathUtilities.CombineAbsoluteAndRelativePaths(ProjectRootPath, fileName));
     }
     else
     {
         var folderPath   = CreateFolderPath(folder);
         var relativePath = PathUtilities.CombinePossiblyRelativeAndRelativePaths(folderPath, fileName);
         return(folderPath, PathUtilities.CombineAbsoluteAndRelativePaths(ProjectRootPath, relativePath));
     }
 }
        private static MetadataFileReferenceResolver CreateFileResolver(ImmutableArray <string> referencePaths, string baseDirectory)
        {
            var userProfilePath   = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            var packagesDirectory = (userProfilePath == null) ?
                                    null :
                                    PathUtilities.CombineAbsoluteAndRelativePaths(userProfilePath, PathUtilities.CombinePossiblyRelativeAndRelativePaths(".nuget", "packages"));

            return(new DesktopMetadataReferenceResolver(
                       new RelativePathReferenceResolver(referencePaths, baseDirectory),
                       string.IsNullOrEmpty(packagesDirectory) ? null : new NuGetPackageResolverImpl(packagesDirectory),
                       new GacFileResolver(
                           architectures: GacFileResolver.Default.Architectures,                 // TODO (tomat)
                           preferredCulture: System.Globalization.CultureInfo.CurrentCulture))); // TODO (tomat)
        }
        internal override ImmutableArray <string> ResolveNuGetPackage(string reference)
        {
            string packageName;
            string packageVersion;

            if (!ParsePackageReference(reference, out packageName, out packageVersion))
            {
                return(default(ImmutableArray <string>));
            }

            try
            {
                var tempPath = PathUtilities.CombineAbsoluteAndRelativePaths(Path.GetTempPath(), Guid.NewGuid().ToString("D"));
                var tempDir  = Directory.CreateDirectory(tempPath);
                try
                {
                    // Create project.json.
                    var projectJson = PathUtilities.CombineAbsoluteAndRelativePaths(tempPath, "project.json");
                    using (var stream = File.OpenWrite(projectJson))
                        using (var writer = new StreamWriter(stream))
                        {
                            WriteProjectJson(writer, packageName, packageVersion);
                        }

                    // Run "nuget.exe restore project.json" to generate project.lock.json.
                    NuGetRestore(projectJson);

                    // Read the references from project.lock.json.
                    var projectLockJson = PathUtilities.CombineAbsoluteAndRelativePaths(tempPath, "project.lock.json");
                    using (var stream = File.OpenRead(projectLockJson))
                        using (var reader = new StreamReader(stream))
                        {
                            return(ReadProjectLockJson(_packagesDirectory, reader));
                        }
                }
                finally
                {
                    tempDir.Delete(recursive: true);
                }
            }
            catch (IOException)
            {
            }
            catch (UnauthorizedAccessException)
            {
            }
            return(default(ImmutableArray <string>));
        }
示例#10
0
        public void TestOpenCloseAnnalyzerConfigDocument()
        {
            var pid     = ProjectId.CreateNewId();
            var text    = SourceText.From("public class C { }");
            var version = VersionStamp.Create();
            var analyzerConfigDocFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(
                Temp.CreateDirectory().Path,
                ".editorconfig"
                );
            var docInfo = DocumentInfo.Create(
                DocumentId.CreateNewId(pid),
                name: ".editorconfig",
                loader: TextLoader.From(
                    TextAndVersion.Create(text, version, analyzerConfigDocFilePath)
                    ),
                filePath: analyzerConfigDocFilePath
                );
            var projInfo = ProjectInfo
                           .Create(
                pid,
                version: VersionStamp.Default,
                name: "TestProject",
                assemblyName: "TestProject.dll",
                language: LanguageNames.CSharp
                )
                           .WithAnalyzerConfigDocuments(new[] { docInfo });

            using (var ws = new AdhocWorkspace())
            {
                ws.AddProject(projInfo);
                var doc = ws.CurrentSolution.GetAnalyzerConfigDocument(docInfo.Id);
                Assert.False(doc.TryGetText(out var currentText));

                ws.OpenAnalyzerConfigDocument(docInfo.Id);

                doc = ws.CurrentSolution.GetAnalyzerConfigDocument(docInfo.Id);
                Assert.True(doc.TryGetText(out currentText));
                Assert.True(doc.TryGetTextVersion(out var currentVersion));
                Assert.Same(text, currentText);
                Assert.Equal(version, currentVersion);

                ws.CloseAnalyzerConfigDocument(docInfo.Id);

                doc = ws.CurrentSolution.GetAnalyzerConfigDocument(docInfo.Id);
                Assert.False(doc.TryGetText(out currentText));
            }
        }
示例#11
0
        private static string?TryGetAnalyzerConfigPathForProjectOrDiagnosticConfiguration(Project project, Diagnostic?diagnostic)
        {
            if (project.AnalyzerConfigDocuments.Any())
            {
                var diagnosticFilePath = PathUtilities.GetDirectoryName(diagnostic?.Location.SourceTree?.FilePath ?? project.FilePath);
                if (!PathUtilities.IsAbsolute(diagnosticFilePath))
                {
                    return(null);
                }

                // Currently, we use a simple heuristic to find existing .editorconfig file.
                // We start from the directory of the source file where the diagnostic was reported and walk up
                // the directory tree to find an .editorconfig file.
                // In future, we might change this algorithm, or allow end users to customize it based on options.

                var bestPath = string.Empty;
                AnalyzerConfigDocument?bestAnalyzerConfigDocument = null;
                foreach (var analyzerConfigDocument in project.AnalyzerConfigDocuments)
                {
                    var analyzerConfigDirectory = PathUtilities.GetDirectoryName(analyzerConfigDocument.FilePath);
                    if (diagnosticFilePath.StartsWith(analyzerConfigDirectory) &&
                        analyzerConfigDirectory.Length > bestPath.Length)
                    {
                        bestPath = analyzerConfigDirectory;
                        bestAnalyzerConfigDocument = analyzerConfigDocument;
                    }
                }

                if (bestAnalyzerConfigDocument != null)
                {
                    return(bestAnalyzerConfigDocument.FilePath);
                }
            }

            // Did not find any existing .editorconfig, so create one at root of the solution, if one exists.
            // If project is not part of a solution, then use project path.
            var solutionOrProjectFilePath = project.Solution?.FilePath ?? project.FilePath;

            if (!PathUtilities.IsAbsolute(solutionOrProjectFilePath))
            {
                return(null);
            }

            var solutionOrProjectDirectoryPath = PathUtilities.GetDirectoryName(solutionOrProjectFilePath);

            return(PathUtilities.CombineAbsoluteAndRelativePaths(solutionOrProjectDirectoryPath, ".editorconfig"));
        }
示例#12
0
        public void CombinePaths()
        {
            Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @""));
            Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", null));
            Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", null));

            Assert.Null(PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"C:\goo"));
            Assert.Null(PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"C:goo"));
            Assert.Null(PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"\goo"));

            Assert.Equal(@"C:\x\y\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x\y", @"goo"));
            Assert.Equal(@"C:\x/y\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"goo"));
            Assert.Equal(@"C:\x/y\.\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @".\goo"));
            Assert.Equal(@"C:\x/y\./goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"./goo"));
            Assert.Equal(@"C:\x/y\..\goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"..\goo"));
            Assert.Equal(@"C:\x/y\../goo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"../goo"));
        }
示例#13
0
        public void CombinePaths()
        {
            Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @""));
            Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", null));
            Assert.Equal(@"C:\x/y", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", null));

            Assert.Equal(null, PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"C:\foo"));
            Assert.Equal(null, PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"C:foo"));
            Assert.Equal(null, PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\", @"\foo"));

            Assert.Equal(@"C:\x\y\foo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x\y", @"foo"));
            Assert.Equal(@"C:\x/y\foo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"foo"));
            Assert.Equal(@"C:\x/y\.\foo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @".\foo"));
            Assert.Equal(@"C:\x/y\./foo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"./foo"));
            Assert.Equal(@"C:\x/y\..\foo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"..\foo"));
            Assert.Equal(@"C:\x/y\../foo", PathUtilities.CombineAbsoluteAndRelativePaths(@"C:\x/y", @"../foo"));
        }
        private ImmutableArray <ModuleMetadata> GetAllModules(ModuleMetadata manifestModule, string assemblyDir)
        {
            List <ModuleMetadata> moduleBuilder = null;

            foreach (string moduleName in manifestModule.GetModuleNames())
            {
                if (moduleBuilder == null)
                {
                    moduleBuilder = new List <ModuleMetadata>();
                    moduleBuilder.Add(manifestModule);
                }

                var module = CreateModuleMetadata(PathUtilities.CombineAbsoluteAndRelativePaths(assemblyDir, moduleName), prefetchEntireImage: false);
                moduleBuilder.Add(module);
            }

            return((moduleBuilder != null) ? moduleBuilder.ToImmutableArray() : ImmutableArray.Create(manifestModule));
        }
        private void NuGetRestore(string projectJsonPath)
        {
            // Load nuget.exe from same directory as current assembly.
            var nugetExePath = PathUtilities.CombineAbsoluteAndRelativePaths(
                PathUtilities.GetDirectoryName(
                    CorLightup.Desktop.GetAssemblyLocation(typeof(NuGetPackageResolverImpl).GetTypeInfo().Assembly)),
                "nuget.exe");
            var startInfo = new ProcessStartInfo()
            {
                FileName               = nugetExePath,
                Arguments              = $"restore \"{projectJsonPath}\" -PackagesDirectory \"{_packagesDirectory}\"",
                CreateNoWindow         = true,
                UseShellExecute        = false,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
            };

            _restore(startInfo);
        }
示例#16
0
        private void NormalizeAndSetBinOutputPathAndRelatedData(string binOutputPath)
        {
            if (binOutputPath != null)
            {
                // Ensure that binOutputPath is either null or a rooted path.
                // CPS might provide such invalid paths during initialization or when project is in unrestored state.
                if (binOutputPath == String.Empty)
                {
                    binOutputPath = null;
                }
                else if (!PathUtilities.IsAbsolute(binOutputPath))
                {
                    // Make it a rooted path.
                    var basePath = this.ContainingDirectoryPathOpt ?? Path.GetTempPath();
                    binOutputPath = PathUtilities.CombineAbsoluteAndRelativePaths(basePath, binOutputPath);
                }
            }

            // We need to ensure that the bin output path for the project has been initialized before we hookup the project with the project tracker.
            SetBinOutputPathAndRelatedData(binOutputPath);
        }
        public async Task NoAction_FileNotRooted()
        {
            var filePath = PathUtilities.CombineAbsoluteAndRelativePaths(PathUtilities.GetPathRoot(ProjectFilePath), "Foo.cs");

            var code =
                $@"
<Workspace>
    <Project Language=""C#"" AssemblyName=""Assembly1"" FilePath=""{ProjectFilePath}"" CommonReferences=""true"">
        <Document FilePath=""{filePath}""> 
namespace [||]NS
{{    
    class Class1
    {{
    }}
}}
        </Document>
    </Project>
</Workspace>";

            await TestMissingInRegularAndScriptAsync(code);
        }
        /// <summary>
        /// Resolves assembly strong name key file path.
        /// Internal for testing.
        /// </summary>
        /// <returns>Normalized key file path or null if not found.</returns>
        internal string ResolveStrongNameKeyFile(string path)
        {
            // Dev11: key path is simply appended to the search paths, even if it starts with the current (parent) directory ("." or "..").
            // This is different from PathUtilities.ResolveRelativePath.

            if (PathUtilities.IsAbsolute(path))
            {
                if (FileExists(path))
                {
                    if (touchedFiles != null)
                    {
                        touchedFiles.AddRead(path);
                    }

                    return(FileUtilities.NormalizeAbsolutePath(path));
                }

                return(path);
            }

            foreach (var searchPath in this.keyFileSearchPaths)
            {
                string combinedPath = PathUtilities.CombineAbsoluteAndRelativePaths(searchPath, path);

                Debug.Assert(combinedPath == null || PathUtilities.IsAbsolute(combinedPath));

                if (FileExists(combinedPath))
                {
                    if (touchedFiles != null)
                    {
                        touchedFiles.AddRead(combinedPath);
                    }

                    return(FileUtilities.NormalizeAbsolutePath(combinedPath));
                }
            }

            return(null);
        }
            /// <summary>
            /// This refactoring only supports non-linked document and linked document in the form of
            /// documents in multi-targeting project. Also for simplicity, we also don't support document
            /// what has different file path and logical path in project (i.e. [ProjectRoot] + `Document.Folders`).
            /// If the requirements above is met, we will return IDs of all documents linked to the specified
            /// document (inclusive), an array of single element will be returned for non-linked document.
            /// </summary>
            private static bool IsSupportedLinkedDocument(Document document, out ImmutableArray <DocumentId> allDocumentIds)
            {
                var solution          = document.Project.Solution;
                var linkedDocumentids = document.GetLinkedDocumentIds();

                // TODO: figure out how to properly determine if and how a document is linked using project system.

                // If we found a linked document which is part of a project with differenct project file,
                // then it's an actual linked file (i.e. not a multi-targeting project). We don't support that, because
                // we don't know which default namespace and folder path we should use to construct target
                // namespace.
                if (linkedDocumentids.Any(id =>
                                          !PathUtilities.PathsEqual(solution.GetDocument(id).Project.FilePath, document.Project.FilePath)))
                {
                    allDocumentIds = default;
                    return(false);
                }

                // Now determine if the actual file path matches its logical path in project
                // which is constructed as <project root path>\Logical\Folders\. The refactoring
                // is triggered only when the two match. The reason of doing this is we don't really know
                // the user's intention of keeping the file path out-of-sync with its logical path.
                var projectRoot = PathUtilities.GetDirectoryName(document.Project.FilePath);
                var folderPath  = Path.Combine(document.Folders.ToArray());

                var absoluteDircetoryPath = PathUtilities.GetDirectoryName(document.FilePath);
                var logicalDirectoryPath  = PathUtilities.CombineAbsoluteAndRelativePaths(projectRoot, folderPath);

                if (PathUtilities.PathsEqual(absoluteDircetoryPath, logicalDirectoryPath))
                {
                    allDocumentIds = linkedDocumentids.Add(document.Id);
                    return(true);
                }
                else
                {
                    allDocumentIds = default;
                    return(false);
                }
            }
示例#20
0
        public CPSProject(
            VisualStudioProjectTracker projectTracker,
            Func <ProjectId, IVsReportExternalErrors> reportExternalErrorCreatorOpt,
            string projectDisplayName,
            string projectFilePath,
            IVsHierarchy hierarchy,
            string language,
            Guid projectGuid,
            string binOutputPath,
            IServiceProvider serviceProvider,
            VisualStudioWorkspaceImpl visualStudioWorkspaceOpt,
            HostDiagnosticUpdateSource hostDiagnosticUpdateSourceOpt,
            ICommandLineParserService commandLineParserServiceOpt)
            : base(projectTracker, reportExternalErrorCreatorOpt, projectDisplayName, projectFilePath,
                   hierarchy, language, projectGuid, serviceProvider, visualStudioWorkspaceOpt, hostDiagnosticUpdateSourceOpt, commandLineParserServiceOpt)
        {
            if (binOutputPath != null)
            {
                // Ensure that binOutputPath is either null or a rooted path.
                // CPS might provide such invalid paths during initialization or when project is in unrestored state.
                if (binOutputPath == String.Empty)
                {
                    binOutputPath = null;
                }
                else if (!PathUtilities.IsAbsolute(binOutputPath))
                {
                    // Make it a rooted path.
                    var basePath = PathUtilities.IsAbsolute(projectFilePath) ? PathUtilities.GetDirectoryName(projectFilePath) : Path.GetTempPath();
                    binOutputPath = PathUtilities.CombineAbsoluteAndRelativePaths(basePath, binOutputPath);
                }
            }

            // We need to ensure that the bin output path for the project has been initialized before we hookup the project with the project tracker.
            SetBinOutputPathAndRelatedData(binOutputPath);

            // Now hook up the project to the project tracker.
            projectTracker.AddProject(this);
        }
示例#21
0
        public void ResolveMetadataFile1()
        {
            string fileName    = "f.dll";
            string drive       = "C";
            string dir         = @"C:\dir";
            string subdir      = @"C:\dir\subdir";
            string filePath    = dir + @"\" + fileName;
            string subFilePath = subdir + @"\" + fileName;
            string dotted      = subdir + @"\" + ".x.dll";

            var fs = new HashSet <string>
            {
                filePath,
                subFilePath,
                dotted
            };

            var resolver = new VirtualizedRelativePathResolver(
                existingFullPaths: fs,
                searchPaths: ImmutableArray.Create <string>(),
                baseDirectory: subdir);

            // unqualified file name:
            var path = resolver.ResolvePath(fileName, baseFilePath: null);

            Assert.Equal(subFilePath, path);

            // prefer the base file over base directory:
            path = resolver.ResolvePath(fileName, baseFilePath: PathUtilities.CombineAbsoluteAndRelativePaths(dir, "foo.csx"));
            Assert.Equal(filePath, path);

            path = resolver.ResolvePath(@"\" + fileName, baseFilePath: null);
            Assert.Equal(null, path);

            path = resolver.ResolvePath(@"/" + fileName, baseFilePath: null);
            Assert.Equal(null, path);

            path = resolver.ResolvePath(@".", baseFilePath: null);
            Assert.Equal(null, path);

            path = resolver.ResolvePath(@".\" + fileName, baseFilePath: null);
            Assert.Equal(subFilePath, path);

            path = resolver.ResolvePath(@"./" + fileName, baseFilePath: null);
            Assert.Equal(subFilePath, path);

            path = resolver.ResolvePath(@".x.dll", baseFilePath: null);
            Assert.Equal(dotted, path);

            path = resolver.ResolvePath(@"..", baseFilePath: null);
            Assert.Equal(null, path);

            path = resolver.ResolvePath(@"..\" + fileName, baseFilePath: null);
            Assert.Equal(filePath, path);

            path = resolver.ResolvePath(@"../" + fileName, baseFilePath: null);
            Assert.Equal(filePath, path);

            path = resolver.ResolvePath(@"C:\" + fileName, baseFilePath: null);
            Assert.Equal(null, path);

            path = resolver.ResolvePath(@"C:/" + fileName, baseFilePath: null);
            Assert.Equal(null, path);

            path = resolver.ResolvePath(filePath, baseFilePath: null);
            Assert.Equal(filePath, path);

            // drive-relative paths not supported:
            path = resolver.ResolvePath(drive + ":" + fileName, baseFilePath: null);
            Assert.Equal(null, path);

            // \abc\def
            string rooted = filePath.Substring(2);

            path = resolver.ResolvePath(rooted, null);
            Assert.Equal(filePath, path);
        }
        // internal for testing
        internal ImmutableArray <CompletionItem> GetItems(string directoryPath, CancellationToken cancellationToken)
        {
            if (!PathUtilities.IsUnixLikePlatform && directoryPath.Length == 1 && directoryPath[0] == '\\')
            {
                // The user has typed only "\".  In this case, we want to add "\\" to the list.
                return(ImmutableArray.Create(CreateNetworkRoot()));
            }

            var result = ArrayBuilder <CompletionItem> .GetInstance();

            var pathKind = PathUtilities.GetPathKind(directoryPath);

            switch (pathKind)
            {
            case PathKind.Empty:
                // base directory
                if (_baseDirectoryOpt != null)
                {
                    result.AddRange(GetItemsInDirectory(_baseDirectoryOpt, cancellationToken));
                }

                // roots
                if (PathUtilities.IsUnixLikePlatform)
                {
                    result.AddRange(CreateUnixRoot());
                }
                else
                {
                    foreach (var drive in GetLogicalDrives())
                    {
                        result.Add(CreateLogicalDriveItem(drive.TrimEnd(s_windowsDirectorySeparator)));
                    }

                    result.Add(CreateNetworkRoot());
                }

                // entries on search paths
                foreach (var searchPath in _searchPaths)
                {
                    result.AddRange(GetItemsInDirectory(searchPath, cancellationToken));
                }

                break;

            case PathKind.Absolute:
            case PathKind.RelativeToCurrentDirectory:
            case PathKind.RelativeToCurrentParent:
            case PathKind.RelativeToCurrentRoot:
                var fullDirectoryPath = FileUtilities.ResolveRelativePath(directoryPath, basePath: null, baseDirectory: _baseDirectoryOpt);
                if (fullDirectoryPath != null)
                {
                    result.AddRange(GetItemsInDirectory(fullDirectoryPath, cancellationToken));
                }
                else
                {
                    // invalid path
                    result.Clear();
                }

                break;

            case PathKind.Relative:

                // base directory:
                if (_baseDirectoryOpt != null)
                {
                    result.AddRange(GetItemsInDirectory(PathUtilities.CombineAbsoluteAndRelativePaths(_baseDirectoryOpt, directoryPath), cancellationToken));
                }

                // search paths:
                foreach (var searchPath in _searchPaths)
                {
                    result.AddRange(GetItemsInDirectory(PathUtilities.CombineAbsoluteAndRelativePaths(searchPath, directoryPath), cancellationToken));
                }

                break;

            case PathKind.RelativeToDriveDirectory:
                // Paths "C:dir" are not supported, but when the path doesn't include any directory, i.e. "C:",
                // we return the drive itself.
                if (directoryPath.Length == 2)
                {
                    result.Add(CreateLogicalDriveItem(directoryPath));
                }

                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(pathKind);
            }

            return(result.ToImmutableAndFree());
        }
        private ImmutableArray <CompletionItem> GetFilesAndDirectories(string path, string basePath)
        {
            var result   = ImmutableArray.CreateBuilder <CompletionItem>();
            var pathKind = PathUtilities.GetPathKind(path);

            switch (pathKind)
            {
            case PathKind.Empty:
                result.Add(CreateCurrentDirectoryItem());

                if (!IsDriveRoot(_fileSystemDiscoveryService.WorkingDirectory))
                {
                    result.Add(CreateParentDirectoryItem());
                }

                result.Add(CreateNetworkRoot(_textChangeSpan));
                result.AddRange(GetLogicalDrives());
                result.AddRange(GetFilesAndDirectoriesInSearchPaths());
                break;

            case PathKind.Absolute:
            case PathKind.RelativeToCurrentDirectory:
            case PathKind.RelativeToCurrentParent:
            case PathKind.RelativeToCurrentRoot:
            {
                var fullPath = FileUtilities.ResolveRelativePath(
                    path,
                    basePath,
                    _fileSystemDiscoveryService.WorkingDirectory);

                if (fullPath != null)
                {
                    result.AddRange(GetFilesAndDirectoriesInDirectory(fullPath));

                    // although it is possible to type "." here, it doesn't make any sense to do so:
                    if (!IsDriveRoot(fullPath) && pathKind != PathKind.Absolute)
                    {
                        result.Add(CreateParentDirectoryItem());
                    }

                    if (path == "\\" && pathKind == PathKind.RelativeToCurrentRoot)
                    {
                        // The user has typed only "\".  In this case, we want to add "\\" to
                        // the list.  Also, the textChangeSpan needs to be backed up by one
                        // so that it will consume the "\" when "\\" is inserted.
                        result.Add(CreateNetworkRoot(TextSpan.FromBounds(_textChangeSpan.Start - 1, _textChangeSpan.End)));
                    }
                }
                else
                {
                    // invalid path
                    result.Clear();
                }
            }

            break;

            case PathKind.Relative:

                // although it is possible to type "." here, it doesn't make any sense to do so:
                result.Add(CreateParentDirectoryItem());

                foreach (var searchPath in _searchPaths)
                {
                    var fullPath = PathUtilities.CombineAbsoluteAndRelativePaths(searchPath, path);

                    // search paths are always absolute:
                    Debug.Assert(PathUtilities.IsAbsolute(fullPath));
                    result.AddRange(GetFilesAndDirectoriesInDirectory(fullPath));
                }

                break;

            case PathKind.RelativeToDriveDirectory:
                // these paths are not supported
                break;

            default:
                throw ExceptionUtilities.Unreachable;
            }

            return(result.AsImmutable());
        }
示例#24
0
        private AbstractProject GetOrCreateProjectFromArgumentsAndReferences(
            IWorkspaceProjectContextFactory workspaceProjectContextFactory,
            IAnalyzerAssemblyLoader analyzerAssemblyLoader,
            string projectFilename,
            IReadOnlyDictionary <string, DeferredProjectInformation> allProjectInfos,
            IReadOnlyDictionary <string, string> targetPathsToProjectPaths)
        {
            var languageName = GetLanguageOfProject(projectFilename);

            if (languageName == null)
            {
                return(null);
            }

            if (!allProjectInfos.TryGetValue(projectFilename, out var projectInfo))
            {
                // This could happen if we were called recursively about a dangling P2P reference
                // that isn't actually in the solution.
                return(null);
            }

            var commandLineParser    = _workspaceServices.GetLanguageServices(languageName).GetService <ICommandLineParserService>();
            var projectDirectory     = PathUtilities.GetDirectoryName(projectFilename);
            var commandLineArguments = commandLineParser.Parse(
                projectInfo.CommandLineArguments,
                projectDirectory,
                isInteractive: false,
                sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory());

            // TODO: Should come from sln file?
            var projectName = PathUtilities.GetFileName(projectFilename, includeExtension: false);

            // `AbstractProject` only sets the filename if it actually exists.  Since we want
            // our ids to match, mimic that behavior here.
            var projectId = File.Exists(projectFilename)
                ? GetOrCreateProjectIdForPath(projectFilename, projectName)
                : GetOrCreateProjectIdForPath(projectName, projectName);

            // See if we've already created this project and we're now in a recursive call to
            // hook up a P2P ref.
            if (_projectMap.TryGetValue(projectId, out var project))
            {
                return(project);
            }

            OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references.");
            var solution5 = _serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution5;

            // If the index is stale, it might give us a path that doesn't exist anymore that the
            // solution doesn't know about - be resilient to that case.
            Guid projectGuid;

            try
            {
                projectGuid = solution5.GetGuidOfProjectFile(projectFilename);
            }
            catch (ArgumentException)
            {
                var message = $"Failed to get the project guid for '{projectFilename}' from the solution, using  random guid instead.";
                Debug.Fail(message);
                OutputToOutputWindow(message);
                projectGuid = Guid.NewGuid();
            }

            // NOTE: If the indexing service fails for a project, it will give us an *empty*
            // target path, which we aren't prepared to handle.  Instead, convert it to a *null*
            // value, which we do handle.
            var outputPath = projectInfo.TargetPath;

            if (outputPath == string.Empty)
            {
                outputPath = null;
            }

            var projectContext = workspaceProjectContextFactory.CreateProjectContext(
                languageName,
                projectName,
                projectFilename,
                projectGuid: projectGuid,
                hierarchy: null,
                binOutputPath: outputPath);

            project = (AbstractProject)projectContext;
            projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" "));

            foreach (var sourceFile in commandLineArguments.SourceFiles)
            {
                projectContext.AddSourceFile(sourceFile.Path);
            }

            foreach (var sourceFile in commandLineArguments.AdditionalFiles)
            {
                projectContext.AddAdditionalFile(sourceFile.Path);
            }

            var addedProjectReferences = new HashSet <string>();

            foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths)
            {
                // NOTE: ImmutableProjects might contain projects for other languages like
                // Xaml, or Typescript where the project file ends up being identical.
                var referencedProject = ImmutableProjects.SingleOrDefault(
                    p => (p.Language == LanguageNames.CSharp || p.Language == LanguageNames.VisualBasic) &&
                    StringComparer.OrdinalIgnoreCase.Equals(p.ProjectFilePath, projectReferencePath));
                if (referencedProject == null)
                {
                    referencedProject = GetOrCreateProjectFromArgumentsAndReferences(
                        workspaceProjectContextFactory,
                        analyzerAssemblyLoader,
                        projectReferencePath,
                        allProjectInfos,
                        targetPathsToProjectPaths);
                }

                var referencedProjectContext = referencedProject as IWorkspaceProjectContext;
                if (referencedProjectContext != null)
                {
                    // TODO: Can we get the properties from corresponding metadata reference in
                    // commandLineArguments?
                    addedProjectReferences.Add(projectReferencePath);
                    projectContext.AddProjectReference(
                        referencedProjectContext,
                        new MetadataReferenceProperties());
                }
                else if (referencedProject != null)
                {
                    // This project was already created by the regular project system. See if we
                    // can find the matching project somehow.
                    var existingReferenceOutputPath = referencedProject?.BinOutputPath;
                    if (existingReferenceOutputPath != null)
                    {
                        addedProjectReferences.Add(projectReferencePath);
                        projectContext.AddMetadataReference(
                            existingReferenceOutputPath,
                            new MetadataReferenceProperties());
                    }
                }
                else
                {
                    // We don't know how to create this project.  Another language or something?
                    OutputToOutputWindow($"Failed to create a project for '{projectReferencePath}'.");
                }
            }

            foreach (var reference in commandLineArguments.ResolveMetadataReferences(project.CurrentCompilationOptions.MetadataReferenceResolver))
            {
                // Some references may fail to be resolved - if they are, we'll still pass them
                // through, in case they come into existence later (they may be built by other
                // parts of the build system).
                var unresolvedReference = reference as UnresolvedMetadataReference;
                var path = unresolvedReference == null
                    ? ((PortableExecutableReference)reference).FilePath
                    : unresolvedReference.Reference;
                if (targetPathsToProjectPaths.TryGetValue(path, out var possibleProjectReference) &&
                    addedProjectReferences.Contains(possibleProjectReference))
                {
                    // We already added a P2P reference for this, we don't need to add the file reference too.
                    continue;
                }

                projectContext.AddMetadataReference(path, reference.Properties);
            }

            foreach (var reference in commandLineArguments.ResolveAnalyzerReferences(analyzerAssemblyLoader))
            {
                var path = reference.FullPath;
                if (!PathUtilities.IsAbsolute(path))
                {
                    path = PathUtilities.CombineAbsoluteAndRelativePaths(
                        projectDirectory,
                        path);
                }

                projectContext.AddAnalyzerReference(path);
            }

            return((AbstractProject)projectContext);
        }
示例#25
0
        private AbstractProject GetOrCreateProjectFromArgumentsAndReferences(
            IWorkspaceProjectContextFactory workspaceProjectContextFactory,
            IAnalyzerAssemblyLoader analyzerAssemblyLoader,
            string projectFilename,
            IReadOnlyDictionary <string, DeferredProjectInformation> allProjectInfos,
            IReadOnlyDictionary <string, string> targetPathsToProjectPaths)
        {
            var languageName = GetLanguageOfProject(projectFilename);

            if (languageName == null)
            {
                return(null);
            }

            if (!allProjectInfos.TryGetValue(projectFilename, out var projectInfo))
            {
                // This could happen if we were called recursively about a dangling P2P reference
                // that isn't actually in the solution.
                return(null);
            }

            // TODO: Should come from .sln file?
            var projectName = PathUtilities.GetFileName(projectFilename, includeExtension: false);

            // `AbstractProject` only sets the filename if it actually exists.  Since we want
            // our ids to match, mimic that behavior here.
            var projectId = File.Exists(projectFilename)
                ? GetOrCreateProjectIdForPath(projectFilename, projectName)
                : GetOrCreateProjectIdForPath(projectName, projectName);

            // See if something has already created this project - it's not deferred, the AnyCode design time build
            // failed so we force loaded it, or we already created a deferred project and we're in a recursive call
            // to find a ProjectReference
            if (_projectMap.TryGetValue(projectId, out var project))
            {
                return(project);
            }

            // If the project system has opted this project out of deferred loading, or AnyCode
            // was unable to get command line info for it, we can't create a project for it.
            // NOTE: We need to check this even though it happened in CreateDeferredProjects
            // because we could be in a recursive call from a project reference below.
            var solution7 = (IVsSolution7)_vsSolution;

            if (DesignTimeBuildFailed(projectInfo) ||
                !solution7.IsDeferredProjectLoadAllowed(projectFilename))
            {
                return(null);
            }

            var commandLineParser    = _workspaceServices.GetLanguageServices(languageName).GetService <ICommandLineParserService>();
            var projectDirectory     = PathUtilities.GetDirectoryName(projectFilename);
            var commandLineArguments = commandLineParser.Parse(
                projectInfo.CommandLineArguments,
                projectDirectory,
                isInteractive: false,
                sdkDirectory: RuntimeEnvironment.GetRuntimeDirectory());

            OutputToOutputWindow($"\tCreating '{projectName}':\t{commandLineArguments.SourceFiles.Length} source files,\t{commandLineArguments.MetadataReferences.Length} references.");

            var projectGuid    = GetProjectGuid(projectFilename);
            var projectContext = workspaceProjectContextFactory.CreateProjectContext(
                languageName,
                projectName,
                projectFilename,
                projectGuid: projectGuid,
                hierarchy: null,
                binOutputPath: projectInfo.TargetPath);

            project = (AbstractProject)projectContext;
            projectContext.SetOptions(projectInfo.CommandLineArguments.Join(" "));

            var addedSourceFilePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var sourceFile in commandLineArguments.SourceFiles)
            {
                if (addedSourceFilePaths.Add(sourceFile.Path))
                {
                    projectContext.AddSourceFile(sourceFile.Path);
                }
            }

            var addedAdditionalFilePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var additionalFile in commandLineArguments.AdditionalFiles)
            {
                if (addedAdditionalFilePaths.Add(additionalFile.Path))
                {
                    projectContext.AddAdditionalFile(additionalFile.Path);
                }
            }

            var metadataReferences     = commandLineArguments.ResolveMetadataReferences(project.CurrentCompilationOptions.MetadataReferenceResolver).AsImmutable();
            var addedProjectReferences = new HashSet <string>();

            foreach (var projectReferencePath in projectInfo.ReferencedProjectFilePaths)
            {
                var referencedProject = TryFindExistingProjectForProjectReference(projectReferencePath, metadataReferences);
                if (referencedProject == null)
                {
                    referencedProject = GetOrCreateProjectFromArgumentsAndReferences(
                        workspaceProjectContextFactory,
                        analyzerAssemblyLoader,
                        projectReferencePath,
                        allProjectInfos,
                        targetPathsToProjectPaths);
                }

                var referencedProjectContext = referencedProject as IWorkspaceProjectContext;
                if (referencedProjectContext != null)
                {
                    // TODO: Can we get the properties from corresponding metadata reference in
                    // commandLineArguments?
                    addedProjectReferences.Add(projectReferencePath);
                    projectContext.AddProjectReference(
                        referencedProjectContext,
                        new MetadataReferenceProperties());
                }
                else if (referencedProject != null)
                {
                    // This project was already created by the regular project system. See if we
                    // can find the matching project somehow.
                    var existingReferenceOutputPath = referencedProject?.BinOutputPath;
                    if (existingReferenceOutputPath != null)
                    {
                        addedProjectReferences.Add(projectReferencePath);
                        projectContext.AddMetadataReference(
                            existingReferenceOutputPath,
                            new MetadataReferenceProperties());
                    }
                }
                else
                {
                    // We don't know how to create this project.  Another language or something?
                    OutputToOutputWindow($"\t\tFailed to create a project for '{projectReferencePath}'.");
                }
            }

            var addedReferencePaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var reference in metadataReferences)
            {
                var path = GetReferencePath(reference);
                if (targetPathsToProjectPaths.TryGetValue(path, out var possibleProjectReference) &&
                    addedProjectReferences.Contains(possibleProjectReference))
                {
                    // We already added a P2P reference for this, we don't need to add the file reference too.
                    continue;
                }

                if (addedReferencePaths.Add(path))
                {
                    projectContext.AddMetadataReference(path, reference.Properties);
                }
            }

            var addedAnalyzerPaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var reference in commandLineArguments.ResolveAnalyzerReferences(analyzerAssemblyLoader))
            {
                var path = reference.FullPath;
                if (!PathUtilities.IsAbsolute(path))
                {
                    path = PathUtilities.CombineAbsoluteAndRelativePaths(
                        projectDirectory,
                        path);
                }

                if (addedAnalyzerPaths.Add(path))
                {
                    projectContext.AddAnalyzerReference(path);
                }
            }

            return((AbstractProject)projectContext);
        }
示例#26
0
        public void ResolveReference()
        {
            var expectedProjectJson =
                @"{
  ""dependencies"": {
    ""A.B.C"": ""1.2""
  },
  ""frameworks"": {
    ""net46"": {}
  }
}";
            var actualProjectLockJson =
                @"{
  ""locked"": false,
  ""version"": 1,
  ""targets"": {
    "".NETFramework,Version=v4.5"": { },
    "".NETFramework,Version=v4.6"": {
      ""System.Collections/4.0.10"": {
        ""dependencies"": {
          ""System.Runtime"": """"
        },
        ""compile"": {
          ""ref/dotnet/System.Runtime.dll"": {}
        },
        ""runtime"": {
          ""ref/dotnet/System.Collections.dll"": {}
        }
      },
      ""System.Diagnostics.Debug/4.0.10"": {
        ""dependencies"": {
          ""System.Runtime"": """"
        },
      },
      ""System.IO/4.0.10"": {
        ""dependencies"": {},
        ""runtime"": {
          ""ref/dotnet/System.Runtime.dll"": {},
          ""ref/dotnet/System.IO.dll"": {}
        }
      }
    }
  }
}";

            using (var directory = new DisposableDirectory(Temp))
            {
                var packagesDirectory = directory.Path;
                var resolver          = new NuGetPackageResolverImpl(
                    packagesDirectory,
                    startInfo =>
                {
                    var arguments = startInfo.Arguments.Split('"');
                    Assert.Equal(5, arguments.Length);
                    Assert.Equal("restore ", arguments[0]);
                    Assert.Equal("project.json", PathUtilities.GetFileName(arguments[1]));
                    Assert.Equal(" -PackagesDirectory ", arguments[2]);
                    Assert.Equal(packagesDirectory, arguments[3]);
                    Assert.Equal("", arguments[4]);
                    var projectJsonPath   = arguments[1];
                    var actualProjectJson = File.ReadAllText(projectJsonPath);
                    Assert.Equal(expectedProjectJson, actualProjectJson);
                    var projectLockJsonPath = PathUtilities.CombineAbsoluteAndRelativePaths(PathUtilities.GetDirectoryName(projectJsonPath), "project.lock.json");
                    using (var writer = new StreamWriter(projectLockJsonPath))
                    {
                        writer.Write(actualProjectLockJson);
                    }
                });
                var actualPaths = resolver.ResolveNuGetPackage("A.B.C/1.2");
                AssertEx.SetEqual(actualPaths,
                                  PathUtilities.CombineAbsoluteAndRelativePaths(packagesDirectory, PathUtilities.CombinePossiblyRelativeAndRelativePaths("System.Collections/4.0.10", "ref/dotnet/System.Collections.dll")),
                                  PathUtilities.CombineAbsoluteAndRelativePaths(packagesDirectory, PathUtilities.CombinePossiblyRelativeAndRelativePaths("System.IO/4.0.10", "ref/dotnet/System.Runtime.dll")),
                                  PathUtilities.CombineAbsoluteAndRelativePaths(packagesDirectory, PathUtilities.CombinePossiblyRelativeAndRelativePaths("System.IO/4.0.10", "ref/dotnet/System.IO.dll")));
            }
        }
示例#27
0
        public void ResolveMetadataFile2()
        {
            string fileName    = "f.dll";
            string dir         = @"C:\dir";
            string subdir      = @"C:\dir\subdir";
            string filePath    = dir + @"\" + fileName;
            string subFilePath = subdir + @"\" + fileName;

            var fs = new HashSet <string>
            {
                filePath,
                subFilePath
            };

            // with no search paths
            var resolver = new VirtualizedRelativePathResolver(
                existingFullPaths: fs,
                baseDirectory: subdir);

            // using base path
            var path = resolver.ResolvePath(fileName, baseFilePath: PathUtilities.CombineAbsoluteAndRelativePaths(dir, "foo.csx"));

            Assert.Equal(filePath, path);

            // using base dir
            path = resolver.ResolvePath(fileName, baseFilePath: null);
            Assert.Equal(subFilePath, path);

            // search paths
            var resolverSP = new VirtualizedRelativePathResolver(
                existingFullPaths: fs,
                searchPaths: new[] { dir, subdir }.AsImmutableOrNull(),
                baseDirectory: @"C:\foo");

            path = resolverSP.ResolvePath(fileName, baseFilePath: null);
            Assert.Equal(filePath, path);

            // null base dir, no search paths
            var resolverNullBase = new VirtualizedRelativePathResolver(
                existingFullPaths: fs,
                baseDirectory: null);

            // relative path
            path = resolverNullBase.ResolvePath(fileName, baseFilePath: null);
            Assert.Null(path);

            // full path
            path = resolverNullBase.ResolvePath(filePath, baseFilePath: null);
            Assert.Equal(filePath, path);

            // null base dir
            var resolverNullBaseSP = new VirtualizedRelativePathResolver(
                existingFullPaths: fs,
                searchPaths: new[] { dir, subdir }.AsImmutableOrNull(),
                baseDirectory: null);

            // relative path
            path = resolverNullBaseSP.ResolvePath(fileName, baseFilePath: null);
            Assert.Equal(filePath, path);

            // full path
            path = resolverNullBaseSP.ResolvePath(filePath, baseFilePath: null);
            Assert.Equal(filePath, path);
        }