From 3364c2066dd1640a319f007d3a5defaebe80015c Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 17 Jun 2026 16:03:08 +0200 Subject: [PATCH 1/3] C#: Fix the cs/path-combine code quality issues in the extractor. --- .../DependencyContainer.cs | 2 +- .../DependencyManager.cs | 4 ++-- .../DotNet.cs | 4 ++-- .../DotNetVersion.cs | 4 ++-- .../NugetExeWrapper.cs | 10 +++++----- .../NugetPackageRestorer.cs | 8 ++++---- .../Runtime.cs | 2 +- .../Semmle.Extraction.CSharp.DependencyFetching/Sdk.cs | 2 +- .../DotnetSourceGeneratorWrapper.cs | 8 ++++---- .../DotnetSourceGeneratorWrapper/Razor.cs | 2 +- .../SourceGenerators/ImplicitUsingsGenerator.cs | 2 +- .../SourceGenerators/ResxGenerator.cs | 2 +- .../SourceGenerators/SourceGeneratorBase.cs | 2 +- .../Extractor/CompilerVersion.cs | 4 ++-- .../Semmle.Extraction.CSharp/Extractor/Context.cs | 2 +- .../Semmle.Extraction.CSharp/Extractor/CsProjFile.cs | 2 +- .../Semmle.Extraction.CSharp/Extractor/Extractor.cs | 8 ++++---- .../Extractor/TracingAnalyser.cs | 2 +- .../Semmle.Extraction.CSharp/Extractor/TrapWriter.cs | 2 +- .../extractor/Semmle.Util.Tests/CanonicalPathCache.cs | 8 ++++---- csharp/extractor/Semmle.Util.Tests/LongPaths.cs | 8 ++++---- csharp/extractor/Semmle.Util/BuildActions.cs | 4 ++-- csharp/extractor/Semmle.Util/CanonicalPathCache.cs | 6 +++--- csharp/extractor/Semmle.Util/FileUtils.cs | 8 ++++---- 24 files changed, 53 insertions(+), 53 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyContainer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyContainer.cs index b5abefb3a651..e99ab4c74f73 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyContainer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyContainer.cs @@ -50,7 +50,7 @@ public void Add(string package, string dependency) return; } - var path = Path.Combine(p, ParseFilePath(d)); + var path = Path.Join(p, ParseFilePath(d)); Paths.Add(path); Packages.Add(GetPackageName(p)); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs index bc010e318c35..2706d5262931 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs @@ -75,7 +75,7 @@ public DependencyManager(string srcDir, ILogger logger) } } - this.diagnosticsWriter = new DiagnosticsStream(Path.Combine( + this.diagnosticsWriter = new DiagnosticsStream(Path.Join( diagDirEnv ?? "", $"dependency-manager-{DateTime.UtcNow:yyyyMMddHHmm}-{Environment.ProcessId}.jsonc")); this.sourceDir = new DirectoryInfo(srcDir); @@ -327,7 +327,7 @@ private void AddNetFrameworkDlls(ISet dllLocations, ISet private void RemoveNugetPackageReference(string packagePrefix, ISet dllLocations) { var packageFolder = nugetPackageRestorer.PackageDirectory.DirInfo.FullName.ToLowerInvariant(); - var packagePathPrefix = Path.Combine(packageFolder, packagePrefix.ToLowerInvariant()); + var packagePathPrefix = Path.Join(packageFolder, packagePrefix.ToLowerInvariant()); var toRemove = dllLocations.Where(s => s.Path.StartsWith(packagePathPrefix, StringComparison.InvariantCultureIgnoreCase)); foreach (var path in toRemove) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs index 699e06d273c8..f8505f8e8fec 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs @@ -31,7 +31,7 @@ private DotNet(IDotNetCliInvoker dotnetCliInvoker, ILogger logger, bool runDotne } } - private DotNet(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, DependabotProxy? dependabotProxy) : this(new DotNetCliInvoker(logger, Path.Combine(dotNetPath ?? string.Empty, "dotnet"), dependabotProxy), logger, dotNetPath is null, tempWorkingDirectory) { } + private DotNet(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, DependabotProxy? dependabotProxy) : this(new DotNetCliInvoker(logger, Path.Join(dotNetPath ?? string.Empty, "dotnet"), dependabotProxy), logger, dotNetPath is null, tempWorkingDirectory) { } internal static IDotNet Make(IDotNetCliInvoker dotnetCliInvoker, ILogger logger, bool runDotnetInfo) => new DotNet(dotnetCliInvoker, logger, runDotnetInfo); @@ -73,7 +73,7 @@ private string GetRestoreArgs(RestoreSettings restoreSettings) var path = ".empty"; if (tempWorkingDirectory != null) { - path = Path.Combine(tempWorkingDirectory.ToString(), "emptyFakeDotnetRoot"); + path = Path.Join(tempWorkingDirectory.ToString(), "emptyFakeDotnetRoot"); Directory.CreateDirectory(path); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetVersion.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetVersion.cs index 31a4ac2292dc..8ea710beb389 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetVersion.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetVersion.cs @@ -12,7 +12,7 @@ internal record DotNetVersion : IComparable private string FullVersion => version.ToString(); - public string FullPath => Path.Combine(dir, FullVersion); + public string FullPath => Path.Join(dir, FullVersion); /** * The full path to the reference assemblies for this runtime. @@ -33,7 +33,7 @@ public string? FullPathReferenceAssemblies { directories[^2] = "packs"; directories[^1] = $"{directories[^1]}.Ref"; - return Path.Combine(string.Join(Path.DirectorySeparatorChar, directories), FullVersion, "ref"); + return Path.Join(string.Join(Path.DirectorySeparatorChar, directories), FullVersion, "ref"); } return null; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs index e97b0b118c68..eb3f2270c16b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetExeWrapper.cs @@ -46,7 +46,7 @@ public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDir if (HasNoPackageSource() && useDefaultFeed()) { // We only modify or add a top level nuget.config file - nugetConfigPath = Path.Combine(fileProvider.SourceDir.FullName, "nuget.config"); + nugetConfigPath = Path.Join(fileProvider.SourceDir.FullName, "nuget.config"); try { if (File.Exists(nugetConfigPath)) @@ -55,7 +55,7 @@ public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDir do { - backupNugetConfig = Path.Combine(tempFolderPath, Path.GetRandomFileName()); + backupNugetConfig = Path.Join(tempFolderPath, Path.GetRandomFileName()); } while (File.Exists(backupNugetConfig)); File.Copy(nugetConfigPath, backupNugetConfig, true); @@ -123,7 +123,7 @@ private string ResolveNugetExe() var nugetPath = FileUtils.FindProgramOnPath(executableName); if (nugetPath is not null) { - nugetPath = Path.Combine(nugetPath, executableName); + nugetPath = Path.Join(nugetPath, executableName); logger.LogInfo($"Using nuget.exe from PATH: {nugetPath}"); return nugetPath; } @@ -133,8 +133,8 @@ private string ResolveNugetExe() private string DownloadNugetExe(string sourceDir) { - var directory = Path.Combine(sourceDir, ".nuget"); - var nuget = Path.Combine(directory, "nuget.exe"); + var directory = Path.Join(sourceDir, ".nuget"); + var nuget = Path.Join(directory, "nuget.exe"); // Nuget.exe already exists in the .nuget directory. if (File.Exists(nuget)) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index e042285af11c..e64c3a14ace0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -209,7 +209,7 @@ public HashSet Restore() var paths = dependencies .Paths - .Select(d => Path.Combine(PackageDirectory.DirInfo.FullName, d)) + .Select(d => Path.Join(PackageDirectory.DirInfo.FullName, d)) .ToList(); assemblyLookupLocations.UnionWith(paths.Select(p => new AssemblyLookupLocation(p))); @@ -527,7 +527,7 @@ private void RestoreProjects(IEnumerable projects, HashSet reach var sb = new StringBuilder(); fallbackNugetFeeds.ForEach((feed, index) => sb.AppendLine($"")); - var nugetConfigPath = Path.Combine(folderPath, "nuget.config"); + var nugetConfigPath = Path.Join(folderPath, "nuget.config"); logger.LogInfo($"Creating fallback nuget.config file {nugetConfigPath}."); File.WriteAllText(nugetConfigPath, $""" @@ -1052,7 +1052,7 @@ public void Dispose() /// private static string ComputeTempDirectoryPath(string subfolderName) { - return Path.Combine(FileUtils.GetTemporaryWorkingDirectory(out _), subfolderName); + return Path.Join(FileUtils.GetTemporaryWorkingDirectory(out _), subfolderName); } /// @@ -1060,7 +1060,7 @@ private static string ComputeTempDirectoryPath(string subfolderName) /// private static string ComputeTempDirectoryPath(string srcDir, string subfolderName) { - return Path.Combine(FileUtils.GetTemporaryWorkingDirectory(out _), FileUtils.ComputeHash(srcDir), subfolderName); + return Path.Join(FileUtils.GetTemporaryWorkingDirectory(out _), FileUtils.ComputeHash(srcDir), subfolderName); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Runtime.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Runtime.cs index 64c835d27fcc..0ed2713b6f95 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Runtime.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Runtime.cs @@ -79,7 +79,7 @@ private IEnumerable DesktopRuntimes var monoPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "mono.exe" : "mono"); string[] monoDirs = monoPath is not null - ? [Path.GetFullPath(Path.Combine(monoPath, "..", "lib", "mono")), monoPath] + ? [Path.GetFullPath(Path.Join(monoPath, "..", "lib", "mono")), monoPath] : ["/usr/lib/mono", "/usr/local/mono", "/usr/local/bin/mono", @"C:\Program Files\Mono\lib\mono"]; var monoDir = monoDirs.FirstOrDefault(Directory.Exists); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Sdk.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Sdk.cs index c4d1ba9ac087..20e188e625ad 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Sdk.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/Sdk.cs @@ -63,7 +63,7 @@ private static HashSet ParseSdks(IList listed) return null; } - var path = Path.Combine(version.FullPath, "Roslyn", "bincore", "csc.dll"); + var path = Path.Join(version.FullPath, "Roslyn", "bincore", "csc.dll"); logger.LogDebug($"Source generator CSC: '{path}'"); if (!File.Exists(path)) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs index 680802449010..9518afac4008 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs @@ -41,10 +41,10 @@ public IEnumerable RunSourceGenerator(IEnumerable additionalFile .Replace('\\', '/'); // Ensure we're generating the same hash regardless of the OS var name = FileUtils.ComputeHash($"{relativePathToCsProj}\n{this.GetType().Name}"); using var tempDir = new TemporaryDirectory(Path.Join(FileUtils.GetTemporaryWorkingDirectory(out _), "source-generator"), "source generator temporary", logger); - var analyzerConfigPath = Path.Combine(tempDir.DirInfo.FullName, $"{name}.txt"); - var dllPath = Path.Combine(tempDir.DirInfo.FullName, $"{name}.dll"); - var cscArgsPath = Path.Combine(tempDir.DirInfo.FullName, $"{name}.rsp"); - var outputFolder = Path.Combine(targetDir, name); + var analyzerConfigPath = Path.Join(tempDir.DirInfo.FullName, $"{name}.txt"); + var dllPath = Path.Join(tempDir.DirInfo.FullName, $"{name}.dll"); + var cscArgsPath = Path.Join(tempDir.DirInfo.FullName, $"{name}.rsp"); + var outputFolder = Path.Join(targetDir, name); Directory.CreateDirectory(outputFolder); logger.LogInfo("Producing analyzer config content."); GenerateAnalyzerConfig(additionalFiles, csprojFile, analyzerConfigPath); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/Razor.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/Razor.cs index 24423e6a129f..fa509bb50b8b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/Razor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/Razor.cs @@ -21,7 +21,7 @@ public Razor(Sdk sdk, IDotNet dotNet, ILogger logger) : base(sdk, dotNet, logger throw new Exception("No SDK path available."); } - SourceGeneratorFolder = Path.Combine(sdkPath, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators"); + SourceGeneratorFolder = Path.Join(sdkPath, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators"); this.logger.LogInfo($"Razor source generator folder: {SourceGeneratorFolder}"); if (!Directory.Exists(SourceGeneratorFolder)) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ImplicitUsingsGenerator.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ImplicitUsingsGenerator.cs index f3bcdae3ac65..4d169b8c9f42 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ImplicitUsingsGenerator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ImplicitUsingsGenerator.cs @@ -50,7 +50,7 @@ protected override IEnumerable Run() if (usings.Count > 0) { var tempDir = GetTemporaryWorkingDirectory("implicitUsings"); - var path = Path.Combine(tempDir, "GlobalUsings.g.cs"); + var path = Path.Join(tempDir, "GlobalUsings.g.cs"); using (var writer = new StreamWriter(path)) { writer.WriteLine("// "); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs index ff24bf0ea6f0..da66ef275442 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs @@ -32,7 +32,7 @@ public ResxGenerator( var nugetFolder = nugetPackageRestorer.TryRestore("Microsoft.CodeAnalysis.ResxSourceGenerator"); if (nugetFolder is not null) { - sourceGeneratorFolder = System.IO.Path.Combine(nugetFolder, "analyzers", "dotnet", "cs"); + sourceGeneratorFolder = System.IO.Path.Join(nugetFolder, "analyzers", "dotnet", "cs"); } } catch (Exception e) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/SourceGeneratorBase.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/SourceGeneratorBase.cs index 36890f2d89c4..545497dbd2ba 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/SourceGeneratorBase.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/SourceGeneratorBase.cs @@ -35,7 +35,7 @@ public IEnumerable Generate() /// protected string GetTemporaryWorkingDirectory(string subfolder) { - var temp = Path.Combine(tempWorkingDirectory.ToString(), subfolder); + var temp = Path.Join(tempWorkingDirectory.ToString(), subfolder); Directory.CreateDirectory(temp); return temp; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CompilerVersion.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CompilerVersion.cs index 5429f2bba075..d894cbbe2ecc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CompilerVersion.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CompilerVersion.cs @@ -67,7 +67,7 @@ public CompilerVersion(Options options) return; } - var mscorlibExists = File.Exists(Path.Combine(compilerDir, "mscorlib.dll")); + var mscorlibExists = File.Exists(Path.Join(compilerDir, "mscorlib.dll")); if (specifiedFramework is null && mscorlibExists) { @@ -107,7 +107,7 @@ private void SkipExtractionBecause(string reason) /// /// The file csc.rsp. /// - private string CscRsp => Path.Combine(FrameworkPath, csc_rsp); + private string CscRsp => Path.Join(FrameworkPath, csc_rsp); /// /// Should we skip extraction? diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs index c37521652046..829561d37ae2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs @@ -680,7 +680,7 @@ public string TryAdjustRelativeMappedFilePath(string mappedToPath, string mapped { try { - var fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(mappedFromPath)!, mappedToPath)); + var fullPath = Path.GetFullPath(Path.Join(Path.GetDirectoryName(mappedFromPath)!, mappedToPath)); ExtractionContext.Logger.LogDebug($"Found relative path in line mapping: '{mappedToPath}', interpreting it as '{fullPath}'"); mappedToPath = fullPath; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs index 665eb0bf3462..d130e4002ff0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs @@ -159,7 +159,7 @@ private static (string[] csFiles, string[] references, string[] projectReference return null; } - return Path.GetFullPath(Path.Combine(projDir?.FullName ?? string.Empty, Path.DirectorySeparatorChar == '/' ? file.Replace("\\", "/") : file)); + return Path.GetFullPath(Path.Join(projDir?.FullName ?? string.Empty, Path.DirectorySeparatorChar == '/' ? file.Replace("\\", "/") : file)); } private readonly string[] references; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs index 69aa7c479097..659fea49e9b0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs @@ -210,7 +210,7 @@ static bool filter(CompilerCall compilerCall) TracingAnalyser.GetOutputName(compilation, args), compilation, generatedSyntaxTrees, - Path.Combine(compilationIdentifierPath, diagnosticName), + Path.Join(compilationIdentifierPath, diagnosticName), options), () => { }); @@ -377,7 +377,7 @@ private static IEnumerable ResolveReferences(Microsoft.CodeAnalysis.Comm else { var composed = referencePaths.Value - .Select(path => Path.Combine(path, clref.Reference)) + .Select(path => Path.Join(path, clref.Reference)) .Where(path => File.Exists(path)) .Select(path => analyser.PathCache.GetCanonicalPath(path)) .FirstOrDefault(); @@ -559,13 +559,13 @@ private static ExitCode AnalyseTracing( /// Gets the path to the `csharp.log` file written to by the C# extractor. /// public static string GetCSharpLogPath() => - Path.Combine(GetCSharpLogDirectory(), "csharp.log"); + Path.Join(GetCSharpLogDirectory(), "csharp.log"); /// /// Gets the path to a `csharp.{hash}.txt` file written to by the C# extractor. /// public static string GetCSharpArgsLogPath(string hash) => - Path.Combine(GetCSharpLogDirectory(), $"csharp.{hash}.txt"); + Path.Join(GetCSharpLogDirectory(), $"csharp.{hash}.txt"); /// /// Gets a list of all `csharp.{hash}.txt` files currently written to the log directory. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs index 9f2a1256f1a1..b66dba798dd1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs @@ -131,7 +131,7 @@ internal static string GetOutputName(CSharpCompilation compilation, return Path.ChangeExtension(entryPointFilename, ".exe"); } - return Path.Combine(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName); + return Path.Join(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName); } private int LogDiagnostics() diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs index 42e933c8eaf4..ea6adb22642c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TrapWriter.cs @@ -61,7 +61,7 @@ public TrapWriter(ILogger logger, PathTransformer.ITransformedPath outputfile, s * Although GetRandomFileName() is cryptographically secure, * there's a tiny chance the file could already exists. */ - tmpFile = Path.Combine(tempPath, Path.GetRandomFileName()); + tmpFile = Path.Join(tempPath, Path.GetRandomFileName()); } while (File.Exists(tmpFile)); diff --git a/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs b/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs index 313b949810dd..e75821a4cb1f 100644 --- a/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs +++ b/csharp/extractor/Semmle.Util.Tests/CanonicalPathCache.cs @@ -82,13 +82,13 @@ public void CanonicalPathUNCRoot() [Fact] public void CanonicalPathMissingFile() { - Assert.Equal(Path.Combine(Directory.GetCurrentDirectory(), "NOSUCHFILE"), cache.GetCanonicalPath("NOSUCHFILE")); + Assert.Equal(Path.Join(Directory.GetCurrentDirectory(), "NOSUCHFILE"), cache.GetCanonicalPath("NOSUCHFILE")); } [Fact] public void CanonicalPathMissingAbsolutePath() { - Assert.Equal(Path.Combine(root, "no", "such", "file"), cache.GetCanonicalPath(Path.Combine(root, "no", "such", "file"))); + Assert.Equal(Path.Join(root, "no", "such", "file"), cache.GetCanonicalPath(Path.Join(root, "no", "such", "file"))); if (Win32.IsWindows()) Assert.Equal(@"C:\Windows\no\such\file", cache.GetCanonicalPath(@"C:\windOws\no\such\file")); @@ -97,7 +97,7 @@ public void CanonicalPathMissingAbsolutePath() [Fact] public void CanonicalPathMissingRelativePath() { - Assert.Equal(Path.Combine(Directory.GetCurrentDirectory(), "NO", "SUCH"), cache.GetCanonicalPath(Path.Combine("NO", "SUCH"))); + Assert.Equal(Path.Join(Directory.GetCurrentDirectory(), "NO", "SUCH"), cache.GetCanonicalPath(Path.Join("NO", "SUCH"))); } [Fact] @@ -125,7 +125,7 @@ public void CanonicalPathCorrectsCase() public void CanonicalPathDots() { var abcPath = Path.GetFullPath("abc"); - Assert.Equal(abcPath, cache.GetCanonicalPath(Path.Combine("foo", ".", "..", "abc"))); + Assert.Equal(abcPath, cache.GetCanonicalPath(Path.Join("foo", ".", "..", "abc"))); } [Fact] diff --git a/csharp/extractor/Semmle.Util.Tests/LongPaths.cs b/csharp/extractor/Semmle.Util.Tests/LongPaths.cs index 90607bc8f02d..381fd97e2145 100644 --- a/csharp/extractor/Semmle.Util.Tests/LongPaths.cs +++ b/csharp/extractor/Semmle.Util.Tests/LongPaths.cs @@ -14,20 +14,20 @@ namespace SemmleTests.Semmle.Util public sealed class LongPaths { private static readonly string tmpDir = Environment.GetEnvironmentVariable("TEST_TMPDIR") ?? Path.GetTempPath(); - private static readonly string longPathDir = Path.Combine(tmpDir, "aaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + private static readonly string longPathDir = Path.Join(tmpDir, "aaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "ccccccccccccccccccccccccccccccc", "ddddddddddddddddddddddddddddddddddddd", "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", "fffffffffffffffffffffffffffffffff", "ggggggggggggggggggggggggggggggggggg", "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"); private static string MakeLongPath() { var uniquePostfix = Guid.NewGuid().ToString("N"); - return Path.Combine(longPathDir, $"iiiiiiiiiiiiiiii{uniquePostfix}.txt"); + return Path.Join(longPathDir, $"iiiiiiiiiiiiiiii{uniquePostfix}.txt"); } private static string MakeShortPath() { var uniquePostfix = Guid.NewGuid().ToString("N"); - return Path.Combine(tmpDir, $"test{uniquePostfix}.txt"); + return Path.Join(tmpDir, $"test{uniquePostfix}.txt"); } public LongPaths() @@ -62,7 +62,7 @@ private static void WithSetUpAndTearDown(Action test) [Fact] public void ParentDirectory() { - Assert.Equal("abc", Path.GetDirectoryName(Path.Combine("abc", "def"))); + Assert.Equal("abc", Path.GetDirectoryName(Path.Join("abc", "def"))); Assert.Equal(Win32.IsWindows() ? "\\" : "/", Path.GetDirectoryName($@"{Path.DirectorySeparatorChar}def")); Assert.Equal("", Path.GetDirectoryName(@"def")); diff --git a/csharp/extractor/Semmle.Util/BuildActions.cs b/csharp/extractor/Semmle.Util/BuildActions.cs index 09696564efc5..94e4abf99978 100644 --- a/csharp/extractor/Semmle.Util/BuildActions.cs +++ b/csharp/extractor/Semmle.Util/BuildActions.cs @@ -137,7 +137,7 @@ public interface IBuildActions bool IsMonoInstalled(); /// - /// Combine path segments, Path.Combine(). + /// Combine path segments, Path.Join(). /// /// The parts of the path. /// The combined path. @@ -293,7 +293,7 @@ bool IBuildActions.IsMonoInstalled() } } - string IBuildActions.PathCombine(params string[] parts) => Path.Combine(parts); + string IBuildActions.PathCombine(params string[] parts) => Path.Join(parts); void IBuildActions.WriteAllText(string filename, string contents) => File.WriteAllText(filename, contents); diff --git a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs index d3cbf41fa101..2dc04e074f62 100644 --- a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs +++ b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs @@ -43,7 +43,7 @@ protected static string ConstructCanonicalPath(string path, IPathCache cache) var parent = Directory.GetParent(path); return parent is not null ? - Path.Combine(cache.GetCanonicalPath(parent.FullName), Path.GetFileName(path)) : + Path.Join(cache.GetCanonicalPath(parent.FullName), Path.GetFileName(path)) : path.ToUpperInvariant(); } } @@ -138,12 +138,12 @@ public override string GetCanonicalPath(string path, IPathCache cache) var entries = Directory.GetFileSystemEntries(parentPath, name); return entries.Length == 1 ? entries[0] - : Path.Combine(parentPath, name); + : Path.Join(parentPath, name); } catch // lgtm[cs/catch-of-all-exceptions] { // IO error or security error querying directory. - return Path.Combine(parentPath, name); + return Path.Join(parentPath, name); } } } diff --git a/csharp/extractor/Semmle.Util/FileUtils.cs b/csharp/extractor/Semmle.Util/FileUtils.cs index ce157a8268a3..4706c18f72b0 100644 --- a/csharp/extractor/Semmle.Util/FileUtils.cs +++ b/csharp/extractor/Semmle.Util/FileUtils.cs @@ -82,7 +82,7 @@ public static void TryDelete(string file) { exes = new[] { prog }; } - var candidates = paths?.Where(path => exes.Any(exe0 => File.Exists(Path.Combine(path, exe0)))); + var candidates = paths?.Where(path => exes.Any(exe0 => File.Exists(Path.Join(path, exe0)))); return candidates?.FirstOrDefault(); } @@ -179,7 +179,7 @@ public static string NestPaths(ILogger logger, string? outerpath, string innerpa { innerpath = ConvertPathToSafeRelativePath(innerpath); - nested = Path.Combine(outerpath, innerpath); + nested = Path.Join(outerpath, innerpath); } try { @@ -203,7 +203,7 @@ public static string NestPaths(ILogger logger, string? outerpath, string innerpa { var tempPath = Path.GetTempPath(); var name = Guid.NewGuid().ToString("N").ToUpper(); - var tempFolder = Path.Combine(tempPath, "GitHub", name); + var tempFolder = Path.Join(tempPath, "GitHub", name); Directory.CreateDirectory(tempFolder); return tempFolder; }); @@ -231,7 +231,7 @@ public static FileInfo CreateTemporaryFile(string extension, out bool shouldClea string outputPath; do { - outputPath = Path.Combine(tempFolder, Path.GetRandomFileName() + extension); + outputPath = Path.Join(tempFolder, Path.GetRandomFileName() + extension); } while (File.Exists(outputPath)); From b2addbf28205b7e2876622f804f5564d354441e6 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 18 Jun 2026 13:53:43 +0200 Subject: [PATCH 2/3] C#: Rename PathCombine to PathJoin. --- .../BuildScripts.cs | 6 +++--- .../Semmle.Autobuild.CSharp/DotNetRule.cs | 2 +- .../Semmle.Autobuild.Cpp.Tests/BuildScripts.cs | 2 +- .../Semmle.Autobuild.Shared/Autobuilder.cs | 2 +- .../Semmle.Autobuild.Shared/BuildTools.cs | 18 +++++++++--------- .../Semmle.Autobuild.Shared/MsBuildRule.cs | 2 +- .../Semmle.Autobuild.Shared/Project.cs | 4 ++-- .../Semmle.Autobuild.Shared/Solution.cs | 2 +- .../DotNet.cs | 6 +++--- csharp/extractor/Semmle.Util/BuildActions.cs | 6 +++--- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index a8ce96539169..d0b06753734f 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -135,7 +135,7 @@ IEnumerable IBuildActions.EnumerateFiles(string dir) if (!EnumerateFiles.TryGetValue(dir, out var str)) throw new ArgumentException("Missing EnumerateFiles " + dir); - return str.Split("\n").Select(p => PathCombine(dir, p)); + return str.Split("\n").Select(p => PathJoin(dir, p)); } public IDictionary EnumerateDirectories { get; } = new Dictionary(); @@ -147,7 +147,7 @@ IEnumerable IBuildActions.EnumerateDirectories(string dir) return string.IsNullOrEmpty(str) ? Enumerable.Empty() - : str.Split("\n").Select(p => PathCombine(dir, p)); + : str.Split("\n").Select(p => PathJoin(dir, p)); } public bool IsWindows { get; set; } @@ -170,7 +170,7 @@ IEnumerable IBuildActions.EnumerateDirectories(string dir) bool IBuildActions.IsMonoInstalled() => IsMonoInstalled; - public string PathCombine(params string[] parts) + public string PathJoin(params string[] parts) { return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p))); } diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index e07f75928872..47dc60b00223 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -109,7 +109,7 @@ public static BuildScript WithDotNet(IAutobuilder builde => WithDotNet(builder, ensureDotNetAvailable: false, (_, env) => f(env)); private static string DotNetCommand(IBuildActions actions, string? dotNetPath) => - dotNetPath is not null ? actions.PathCombine(dotNetPath, "dotnet") : "dotnet"; + dotNetPath is not null ? actions.PathJoin(dotNetPath, "dotnet") : "dotnet"; private static CommandBuilder GetCleanCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) { diff --git a/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs index fd5e4073d6d9..661701f2b95a 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs @@ -158,7 +158,7 @@ IEnumerable IBuildActions.EnumerateDirectories(string dir) bool IBuildActions.IsMonoInstalled() => IsMonoInstalled; - string IBuildActions.PathCombine(params string[] parts) + string IBuildActions.PathJoin(params string[] parts) { return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p))); } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs index a15235d35021..a26254f7d19c 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs @@ -108,7 +108,7 @@ public abstract class Autobuilder : IDisposable, IAutobuilder /// /// The relative path. /// True iff the path was found. - public bool HasRelativePath(string path) => HasPath(Actions.PathCombine(RootDirectory, path)); + public bool HasRelativePath(string path) => HasPath(Actions.PathJoin(RootDirectory, path)); /// /// List of project/solution files to build. diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs index c445fcb805a0..83779b2a8d9b 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs @@ -32,7 +32,7 @@ public static IEnumerable GetCandidateVcVarsFiles(IBuildActions a yield break; // Attempt to use vswhere to find installations of Visual Studio - var vswhere = actions.PathCombine(programFilesx86, "Microsoft Visual Studio", "Installer", "vswhere.exe"); + var vswhere = actions.PathJoin(programFilesx86, "Microsoft Visual Studio", "Installer", "vswhere.exe"); if (actions.FileExists(vswhere)) { @@ -51,14 +51,14 @@ public static IEnumerable GetCandidateVcVarsFiles(IBuildActions a if (majorVersion < 15) { // Visual Studio 2015 and below - yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\vcvarsall.bat"), majorVersion); + yield return new VcVarsBatFile(actions.PathJoin(vsInstallation.InstallationPath, @"VC\vcvarsall.bat"), majorVersion); } else { // Visual Studio 2017 and above - yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars32.bat"), majorVersion); - yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars64.bat"), majorVersion); - yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"Common7\Tools\VsDevCmd.bat"), majorVersion); + yield return new VcVarsBatFile(actions.PathJoin(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars32.bat"), majorVersion); + yield return new VcVarsBatFile(actions.PathJoin(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars64.bat"), majorVersion); + yield return new VcVarsBatFile(actions.PathJoin(vsInstallation.InstallationPath, @"Common7\Tools\VsDevCmd.bat"), majorVersion); } } // else: Skip installation without a version @@ -68,10 +68,10 @@ public static IEnumerable GetCandidateVcVarsFiles(IBuildActions a } // vswhere not installed or didn't run correctly - return legacy Visual Studio versions - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 14.0\VC\vcvarsall.bat"), 14); - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 12.0\VC\vcvarsall.bat"), 12); - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 11.0\VC\vcvarsall.bat"), 11); - yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 10.0\VC\vcvarsall.bat"), 10); + yield return new VcVarsBatFile(actions.PathJoin(programFilesx86, @"Microsoft Visual Studio 14.0\VC\vcvarsall.bat"), 14); + yield return new VcVarsBatFile(actions.PathJoin(programFilesx86, @"Microsoft Visual Studio 12.0\VC\vcvarsall.bat"), 12); + yield return new VcVarsBatFile(actions.PathJoin(programFilesx86, @"Microsoft Visual Studio 11.0\VC\vcvarsall.bat"), 11); + yield return new VcVarsBatFile(actions.PathJoin(programFilesx86, @"Microsoft Visual Studio 10.0\VC\vcvarsall.bat"), 10); } /// diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs index 748a22fb9d3c..62d4f426db50 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs @@ -60,7 +60,7 @@ public BuildScript Analyse(IAutobuilder builder, bool au // Use `nuget.exe` from source code repo, if present, otherwise first attempt with global // `nuget` command, and if that fails, attempt to download `nuget.exe` from nuget.org var nuget = builder.GetFilename("nuget.exe").Select(t => t.Item1).FirstOrDefault() ?? "nuget"; - var nugetDownloadPath = builder.Actions.PathCombine(FileUtils.GetTemporaryWorkingDirectory(builder.Actions.GetEnvironmentVariable, builder.Options.Language.UpperCaseName, out _), ".nuget", "nuget.exe"); + var nugetDownloadPath = builder.Actions.PathJoin(FileUtils.GetTemporaryWorkingDirectory(builder.Actions.GetEnvironmentVariable, builder.Options.Language.UpperCaseName, out _), ".nuget", "nuget.exe"); var nugetDownloaded = false; var ret = BuildScript.Success; diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs index 32c5cdeca28d..4a6acb127e35 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs @@ -107,8 +107,8 @@ public Project(Autobuilder builder, string path) : base(build continue; } - var includePath = builder.Actions.PathCombine(include.Value.Split('\\', StringSplitOptions.RemoveEmptyEntries)); - ret.Add(new Project(builder, builder.Actions.PathCombine(DirectoryName, includePath))); + var includePath = builder.Actions.PathJoin(include.Value.Split('\\', StringSplitOptions.RemoveEmptyEntries)); + ret.Add(new Project(builder, builder.Actions.PathJoin(DirectoryName, includePath))); } return ret; }); diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs index 5cf0c4a8487f..5852a3834d41 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs @@ -79,7 +79,7 @@ public Solution(Autobuilder builder, string path, bool allowP includedProjects = solution.ProjectsInOrder .Where(p => p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat) - .Select(p => builder.Actions.PathCombine(DirectoryName, builder.Actions.PathCombine(p.RelativePath.Split('\\', StringSplitOptions.RemoveEmptyEntries)))) + .Select(p => builder.Actions.PathJoin(DirectoryName, builder.Actions.PathJoin(p.RelativePath.Split('\\', StringSplitOptions.RemoveEmptyEntries)))) .Select(p => new Project(builder, p)) .ToArray(); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs index f8505f8e8fec..e93a29336126 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs @@ -303,7 +303,7 @@ BuildScript GetInstall(string pwsh) => } else { - var dotnetInstallPath = actions.PathCombine(tempWorkingDirectory, ".dotnet", "dotnet-install.sh"); + var dotnetInstallPath = actions.PathJoin(tempWorkingDirectory, ".dotnet", "dotnet-install.sh"); var downloadDotNetInstallSh = BuildScript.DownloadFile( "https://dot.net/v1/dotnet-install.sh", dotnetInstallPath, @@ -339,7 +339,7 @@ BuildScript GetInstall(string pwsh) => }; } - var dotnetInfo = InfoScript(actions, actions.PathCombine(path, "dotnet"), MinimalEnvironment.ToDictionary(), logger); + var dotnetInfo = InfoScript(actions, actions.PathJoin(path, "dotnet"), MinimalEnvironment.ToDictionary(), logger); Func getInstallAndVerify = version => // run `dotnet --info` after install, to check that it executes successfully @@ -384,7 +384,7 @@ private static BuildScript GetInstalledSdksScript(IBuildActions actions) /// public static BuildScript WithDotNet(IBuildActions actions, ILogger logger, IEnumerable files, string tempWorkingDirectory, bool shouldCleanUp, bool ensureDotNetAvailable, string? version, Func f) { - var installDir = actions.PathCombine(tempWorkingDirectory, ".dotnet"); + var installDir = actions.PathJoin(tempWorkingDirectory, ".dotnet"); var installScript = DownloadDotNet(actions, logger, files, tempWorkingDirectory, shouldCleanUp, installDir, version, ensureDotNetAvailable); return BuildScript.Bind(installScript, installed => { diff --git a/csharp/extractor/Semmle.Util/BuildActions.cs b/csharp/extractor/Semmle.Util/BuildActions.cs index 94e4abf99978..20c5ee708412 100644 --- a/csharp/extractor/Semmle.Util/BuildActions.cs +++ b/csharp/extractor/Semmle.Util/BuildActions.cs @@ -137,11 +137,11 @@ public interface IBuildActions bool IsMonoInstalled(); /// - /// Combine path segments, Path.Join(). + /// Joins path segments, Path.Join(). /// /// The parts of the path. /// The combined path. - string PathCombine(params string[] parts); + string PathJoin(params string[] parts); /// /// Gets the full path for , Path.GetFullPath(). @@ -293,7 +293,7 @@ bool IBuildActions.IsMonoInstalled() } } - string IBuildActions.PathCombine(params string[] parts) => Path.Join(parts); + string IBuildActions.PathJoin(params string[] parts) => Path.Join(parts); void IBuildActions.WriteAllText(string filename, string contents) => File.WriteAllText(filename, contents); From 27c19007d276572c14e5ae3835f5a4dc83050805 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 18 Jun 2026 15:29:03 +0200 Subject: [PATCH 3/3] C#: Handle the places where we could risk that Path.Combine would have thrown away the first argument. --- csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs | 3 ++- .../Semmle.Extraction.CSharp/Extractor/CsProjFile.cs | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs index 4a6acb127e35..9c2902f89732 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs @@ -108,7 +108,8 @@ public Project(Autobuilder builder, string path) : base(build } var includePath = builder.Actions.PathJoin(include.Value.Split('\\', StringSplitOptions.RemoveEmptyEntries)); - ret.Add(new Project(builder, builder.Actions.PathJoin(DirectoryName, includePath))); + var path = Path.IsPathRooted(includePath) ? includePath : builder.Actions.PathJoin(DirectoryName, includePath); + ret.Add(new Project(builder, path)); } return ret; }); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs index d130e4002ff0..844f16086c6f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs @@ -159,7 +159,11 @@ private static (string[] csFiles, string[] references, string[] projectReference return null; } - return Path.GetFullPath(Path.Join(projDir?.FullName ?? string.Empty, Path.DirectorySeparatorChar == '/' ? file.Replace("\\", "/") : file)); + var normalized = Path.DirectorySeparatorChar == '/' ? file.Replace("\\", "/") : file; + var path = projDir is not null && !Path.IsPathRooted(normalized) + ? Path.Join(projDir.FullName, normalized) + : normalized; + return Path.GetFullPath(path); } private readonly string[] references;