Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SLVS-1638 Migrate CMake analysis to CFamily java plugin via SLCore #5855

Merged
merged 6 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
390 changes: 0 additions & 390 deletions src/CFamily.UnitTests/Analysis/CLangAnalyzerTests.cs

This file was deleted.

209 changes: 9 additions & 200 deletions src/CFamily/Analysis/CLangAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,7 @@
*/

using System.ComponentModel.Composition;
using System.IO;
using System.IO.Abstractions;
using Microsoft.VisualStudio.Threading;
using SonarLint.VisualStudio.CFamily.SubProcess;
using SonarLint.VisualStudio.Core;
using SonarLint.VisualStudio.Core.Analysis;
using SonarLint.VisualStudio.Core.Telemetry;
using SonarLint.VisualStudio.Infrastructure.VS;
using SonarLint.VisualStudio.Integration;
using Task = System.Threading.Tasks.Task;

namespace SonarLint.VisualStudio.CFamily.Analysis
{
Expand All @@ -42,220 +33,38 @@ void ExecuteAnalysis(string path,
CancellationToken cancellationToken);
}

[Export(typeof(IAnalyzer))]
[Export(typeof(ICFamilyAnalyzer))]
[PartCreationPolicy(CreationPolicy.Shared)]
internal class CLangAnalyzer : ICFamilyAnalyzer
{
private readonly ITelemetryManager telemetryManager;
private readonly ISonarLintSettings settings;
private readonly IAnalysisStatusNotifierFactory analysisStatusNotifierFactory;
private readonly ILogger logger;
private readonly ICFamilyIssueConverterFactory issueConverterFactory;
private readonly IRequestFactoryAggregate requestFactory;
private readonly IFileSystem fileSystem;
private readonly IThreadHandling threadHandling;

[ImportingConstructor]
public CLangAnalyzer(ITelemetryManager telemetryManager,
ISonarLintSettings settings,
IAnalysisStatusNotifierFactory analysisStatusNotifierFactory,
ICFamilyIssueConverterFactory issueConverterFactory,
IRequestFactoryAggregate requestFactory,
ILogger logger)
: this(telemetryManager, settings, analysisStatusNotifierFactory, issueConverterFactory, requestFactory, logger, new FileSystem(), ThreadHandling.Instance)
{
}

internal /* for testing */ CLangAnalyzer(ITelemetryManager telemetryManager,
ISonarLintSettings settings,
IAnalysisStatusNotifierFactory analysisStatusNotifierFactory,
ICFamilyIssueConverterFactory issueConverterFactory,
IRequestFactoryAggregate requestFactory,
ILogger logger,
IFileSystem fileSystem,
IThreadHandling threadHandling)

public CLangAnalyzer()
{
this.telemetryManager = telemetryManager;
this.settings = settings;
this.analysisStatusNotifierFactory = analysisStatusNotifierFactory;
this.logger = logger;
this.issueConverterFactory = issueConverterFactory;
this.requestFactory = requestFactory;
this.fileSystem = fileSystem;
this.threadHandling = threadHandling;
}

public bool IsAnalysisSupported(IEnumerable<AnalysisLanguage> languages)
{
return languages.Contains(AnalysisLanguage.CFamily);
}

public void ExecuteAnalysis(string path, Guid analysisId, IEnumerable<AnalysisLanguage> detectedLanguages,
IIssueConsumer consumer, IAnalyzerOptions analyzerOptions,
CancellationToken cancellationToken)
{
var analysisStatusNotifier = analysisStatusNotifierFactory.Create(nameof(CLangAnalyzer), path);

ExecuteAnalysis(path, detectedLanguages, consumer, analyzerOptions, analysisStatusNotifier, cancellationToken);
}

public void ExecuteAnalysis(string path,
public void ExecuteAnalysis(
string path,
Guid analysisId,
IEnumerable<AnalysisLanguage> detectedLanguages,
IIssueConsumer consumer,
IAnalyzerOptions analyzerOptions,
IAnalysisStatusNotifier statusNotifier,
CancellationToken cancellationToken) =>
TriggerAnalysisAsync(path, detectedLanguages, consumer, analyzerOptions, statusNotifier, cancellationToken)
.Forget(); // fire and forget
CancellationToken cancellationToken)
{
}

internal /* for testing */ async Task TriggerAnalysisAsync(string path,
public void ExecuteAnalysis(
string path,
IEnumerable<AnalysisLanguage> detectedLanguages,
IIssueConsumer consumer,
IAnalyzerOptions analyzerOptions,
IAnalysisStatusNotifier statusNotifier,
CancellationToken cancellationToken)
{
Debug.Assert(IsAnalysisSupported(detectedLanguages));

// Switch to a background thread
await threadHandling.SwitchToBackgroundThread();

var request = await TryCreateRequestAsync(path, analyzerOptions);

if (request != null)
{
RunAnalysis(request, consumer, statusNotifier, cancellationToken);
}
}

private async Task<IRequest> TryCreateRequestAsync(string path, IAnalyzerOptions analyzerOptions)
{
var cFamilyAnalyzerOptions = analyzerOptions as CFamilyAnalyzerOptions;
var request = await requestFactory.TryCreateAsync(path, cFamilyAnalyzerOptions);

if (request == null)
{
// Logging for PCH is too noisy: #2553
if (cFamilyAnalyzerOptions == null || !cFamilyAnalyzerOptions.CreatePreCompiledHeaders)
{
logger.WriteLine(CFamilyStrings.MSG_UnableToCreateConfig, path);
}
return null;
}

return request;
}

protected /* for testing */ virtual void CallSubProcess(Action<Message> handleMessage, IRequest request, ISonarLintSettings settings, ILogger logger, CancellationToken cancellationToken)
{
ExecuteSubProcess(handleMessage, request, new ProcessRunner(settings, logger), logger, cancellationToken, fileSystem);
}

private void RunAnalysis(IRequest request, IIssueConsumer consumer, IAnalysisStatusNotifier statusNotifier, CancellationToken cancellationToken)
{
var analysisStopwatch = Stopwatch.StartNew();
statusNotifier?.AnalysisStarted();

var messageHandler = consumer == null
? NoOpMessageHandler.Instance
: new MessageHandler(request, consumer, issueConverterFactory.Create(), logger);

try
{
// We're tying up a background thread waiting for out-of-process analysis. We could
// change the process runner so it works asynchronously. Alternatively, we could change the
// RequestAnalysis method to be asynchronous, rather than fire-and-forget.
CallSubProcess(messageHandler.HandleMessage, request, settings, logger, cancellationToken);

if (cancellationToken.IsCancellationRequested)
{
statusNotifier?.AnalysisCancelled();
}
else
{
if (messageHandler.AnalysisSucceeded)
{
statusNotifier?.AnalysisFinished(messageHandler.IssueCount, analysisStopwatch.Elapsed);
}
else
{
statusNotifier?.AnalysisFailed(CFamilyStrings.MSG_GenericAnalysisFailed);
}
}
}
catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex))
{
statusNotifier?.AnalysisFailed(ex);
}

telemetryManager.LanguageAnalyzed(request.Context.CFamilyLanguage, analysisStopwatch.Elapsed); // different keys for C and C++
}

internal /* for testing */ static void ExecuteSubProcess(Action<Message> handleMessage, IRequest request, IProcessRunner runner, ILogger logger, CancellationToken cancellationToken, IFileSystem fileSystem)
{
if (SubProcessFilePaths.AnalyzerExeFilePath == null)
{
logger.WriteLine(CFamilyStrings.MSG_UnableToLocateSubProcessExe);
return;
}

var createReproducer = request.Context.AnalyzerOptions?.CreateReproducer ?? false;
if (createReproducer)
{
SaveRequestDiagnostics(request, logger, fileSystem);
}

const string communicateViaStreaming = "-"; // signal the subprocess we want to communicate via standard IO streams.

var args = new ProcessRunnerArguments(SubProcessFilePaths.AnalyzerExeFilePath, false)
{
CmdLineArgs = new[] { communicateViaStreaming },
CancellationToken = cancellationToken,
WorkingDirectory = SubProcessFilePaths.WorkingDirectory,
EnvironmentVariables = request.EnvironmentVariables,
HandleInputStream = writer =>
{
using (var binaryWriter = new BinaryWriter(writer.BaseStream))
{
request.WriteRequest(binaryWriter);
}
},
HandleOutputStream = reader =>
{
if (createReproducer)
{
reader.ReadToEnd();
logger.WriteLine(CFamilyStrings.MSG_ReproducerSaved, SubProcessFilePaths.ReproducerFilePath);
}
else if (request.Context.AnalyzerOptions?.CreatePreCompiledHeaders ?? false)
{
reader.ReadToEnd();
logger.WriteLine(CFamilyStrings.MSG_PchSaved, request.Context.File, request.Context.PchFile);
}
else
{
using (var binaryReader = new BinaryReader(reader.BaseStream))
{
Protocol.Read(binaryReader, handleMessage);
}
}
}
};

runner.Execute(args);
}

private static void SaveRequestDiagnostics(IRequest request, ILogger logger, IFileSystem fileSystem)
{
using (var stream = fileSystem.FileStream.Create(SubProcessFilePaths.RequestConfigFilePath, FileMode.Create, FileAccess.Write))
using (var writer = new StreamWriter(stream))
{
request.WriteRequestDiagnostics(writer);
}

logger.WriteLine(CFamilyStrings.MSG_RequestConfigSaved, SubProcessFilePaths.RequestConfigFilePath);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ public void AnalyzableLanguages_ShouldBeExpected()
Language.JS,
Language.TS,
Language.CSS,
Language.C,
Language.CPP,
Language.SECRETS
};

Expand Down
2 changes: 2 additions & 0 deletions src/Integration/SLCore/SLCoreConstantsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public SLCoreConstantsProvider(IVsInfoProvider vsInfoProvider)
Language.JS,
Language.TS,
Language.CSS,
Language.C,
Language.CPP,
Language.SECRETS
];

Expand Down
Loading