Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
18 changes: 18 additions & 0 deletions src/CFamily.UnitTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,22 @@
"resolved": "16.5.0",
"contentHash": "K0hfdWy+0p8DJXxzpNc4T5zHm4hf9QONAvyzvw3utKExmxRBShtV/+uHVYTblZWk+rIHNEHeglyXMmqfSshdFA=="
},
"Microsoft.CodeAnalysis.CSharp": {
"type": "Transitive",
"resolved": "3.11.0",
"contentHash": "aDRRb7y/sXoJyDqFEQ3Il9jZxyUMHkShzZeCRjQf3SS84n2J0cTEi3TbwVZE9XJvAeMJhGfVVxwOdjYBg6ljmw==",
"dependencies": {
"Microsoft.CodeAnalysis.Common": "[3.11.0]"
}
},
"Microsoft.CodeAnalysis.VisualBasic": {
"type": "Transitive",
"resolved": "3.11.0",
"contentHash": "i+BGiANEBvkKfhx0NChsMtSmAlhjBtRRkx77LLCgH15pkaCv+H3gfCAJY9FWfK+34691BGp07c45Ml4xulXT8g==",
"dependencies": {
"Microsoft.CodeAnalysis.Common": "[3.11.0]"
}
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "16.6.1",
Expand Down Expand Up @@ -1369,6 +1385,8 @@
"SonarLint.VisualStudio.RoslynAnalyzerServer": {
"type": "Project",
"dependencies": {
"Microsoft.CodeAnalysis.CSharp": "[3.11.0, )",
"Microsoft.CodeAnalysis.VisualBasic": "[3.11.0, )",
"SonarLint.VisualStudio.Core": "[1.0.0, )"
}
},
Expand Down
28 changes: 5 additions & 23 deletions src/Core/Analysis/IAnalysisRequester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarLint.VisualStudio.Core.Analysis
{
public interface IAnalysisRequester
{
/// <summary>
/// Notification that analysis has been requested
/// </summary>
event EventHandler<AnalysisRequestEventArgs> AnalysisRequested;
namespace SonarLint.VisualStudio.Core.Analysis;

/// <summary>
/// Called to request that analysis is performed
/// </summary>
/// <param name="filePaths">List of specific files to analyze. Can be null, in which case all opened files will be analyzed.</param>
/// <remarks>There are no guarantees about whether the analysis is performed before the method
/// returns or not.</remarks>
void RequestAnalysis(params string[] filePaths);
}
public interface IAnalysisRequester
{
event EventHandler<EventArgs> AnalysisRequested;

public class AnalysisRequestEventArgs(IEnumerable<string> filePaths) : EventArgs
{
/// <summary>
/// The list of files to analyze. Null/empty = analyze all files
/// </summary>
public IEnumerable<string> FilePaths { get; } = filePaths;
}
void QueueAnalyzeOpenFiles();
}
6 changes: 2 additions & 4 deletions src/Core/Analysis/IIssueConsumerStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,16 @@ namespace SonarLint.VisualStudio.Core.Analysis;
public interface IIssueConsumerStorage
{
/// <summary>
/// Sets the latest <paramref name="analysisId"/> for <paramref name="filePath"/> and associated <paramref name="issueConsumer"/>
/// Sets the latest <paramref name="issueConsumer"/> for <paramref name="filePath"/>
/// </summary>
/// <param name="filePath">File system path for which the analysis was scheduled</param>
/// <param name="analysisId">Unique analysis identifier</param>
/// <param name="issueConsumer">Consumer for analysis results</param>
void Set(string filePath, IIssueConsumer issueConsumer);

/// <summary>
/// Gets the latest <paramref name="analysisId"/> for <paramref name="filePath"/> and associated <paramref name="issueConsumer"/>
/// Gets the latest <paramref name="issueConsumer"/> for <paramref name="filePath"/>
/// </summary>
/// <param name="filePath">File system path for which the analysis was scheduled</param>
/// <param name="analysisId">Unique analysis identifier</param>
/// <param name="issueConsumer">Consumer for analysis results</param>
/// <returns>true if analysis is scheduled for the given <paramref name="filePath"/>, false otherwise</returns>
bool TryGet(string filePath, out IIssueConsumer issueConsumer);
Expand Down
1 change: 1 addition & 0 deletions src/Core/Analysis/IssueConsumerStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public IssueConsumerStorage()
internalStorage = new Dictionary<string, IIssueConsumer>();
}


public void Remove(string filePath)
{
lock (Lock)
Expand Down
4 changes: 1 addition & 3 deletions src/Core/DocumentEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@

namespace SonarLint.VisualStudio.Core;

public class DocumentEventArgs(Document document, string content = null) : EventArgs
public class DocumentEventArgs(Document document) : EventArgs
{
public Document Document { get; } = document;
public string Content { get; } = content;
}

public class DocumentRenamedEventArgs(Document document, string oldFilePath) : DocumentEventArgs(document)
Expand All @@ -47,7 +46,6 @@ public interface IDocumentTracker
event EventHandler<DocumentEventArgs> DocumentClosed;
event EventHandler<DocumentEventArgs> DocumentOpened;
event EventHandler<DocumentEventArgs> DocumentSaved;
event EventHandler<DocumentEventArgs> DocumentUpdated;
/// <summary>
/// Raised when an opened document is renamed
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,33 +230,6 @@ public void Detect_WhenContentTypeIsTypeScriptButFileExtensionIsJavaScript_Retur
result.First().Should().Be(AnalysisLanguage.Javascript);
}

[DataRow("cs", AnalysisLanguage.RoslynFamily)]
[DataRow("vb", AnalysisLanguage.RoslynFamily)]
[DataRow("js", AnalysisLanguage.Javascript)]
[DataRow("ts", AnalysisLanguage.TypeScript)]
[DataRow("cpp", AnalysisLanguage.CFamily)]
[DataRow("css", AnalysisLanguage.CascadingStyleSheets)]
[DataRow("scss", AnalysisLanguage.CascadingStyleSheets)]
[DataRow("less", AnalysisLanguage.CascadingStyleSheets)]
[TestMethod]
public void GetAnalysisLanguageFromExtension_ReturnsAnalysisLangFromExtension(string fileName, AnalysisLanguage expectedLanguage)
{
var actualLanguage = testSubject.GetAnalysisLanguageFromExtension(fileName);

actualLanguage.Should().NotBeNull();
actualLanguage.Value.Should().Be(expectedLanguage);
}

[DataRow("json")]
[DataRow("")]
[DataRow(null)]
public void GetAnalysisLanguageFromExtension_UnknownExtensionPassed_ReturnsNull(string fileName)
{
var actualLanguage = testSubject.GetAnalysisLanguageFromExtension(fileName);

actualLanguage.Should().BeNull();
}

private void FileExtensionServiceSetup()
{
ContentTypesSetup();
Expand Down
64 changes: 10 additions & 54 deletions src/Infrastructure.VS/LanguageDetection/SonarLanguageRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.Utilities;
using SonarLint.VisualStudio.Core.Analysis;

Expand All @@ -31,13 +28,6 @@ namespace SonarLint.VisualStudio.IssueVisualization.Editor.LanguageDetection
public interface ISonarLanguageRecognizer
{
IEnumerable<AnalysisLanguage> Detect(string filePath, IContentType bufferContentType);

/// <summary>
/// Returns Language associated with the extension
/// </summary>
/// <param name="fileExtension">file extension in lower case without "."</param>
/// <returns>AnalysisLanguage or null if not recognized</returns>
AnalysisLanguage? GetAnalysisLanguageFromExtension(string fileExtension);
}

[Export(typeof(ISonarLanguageRecognizer))]
Expand All @@ -48,8 +38,9 @@ internal class SonarLanguageRecognizer : ISonarLanguageRecognizer

private const string CFamilyTypeName = "C/C++";
private const string TypeScriptTypeName = "TypeScript";
private const string CSharpTypeName = "CSharp";
private const string BasicTypeName = "Basic";
private const string JavascriptTypeName = "JavaScript";
private const string VueTypeName = "Vue";
private const string RoslynLanguagesTypeName = "Roslyn Languages";
private const string CSSTypeName = "css";
private const string SCSSTypeName = "SCSS";
private const string LESSTypeName = "LESS";
Expand Down Expand Up @@ -100,7 +91,7 @@ public IEnumerable<AnalysisLanguage> Detect(string filePath, IContentType buffer
return detectedLanguages;
}

private IEnumerable<IContentType> GetExtensionContentTypes(string fileExtension, IContentType bufferContentType)
private List<IContentType> GetExtensionContentTypes(string fileExtension, IContentType bufferContentType)
{
var contentTypes = contentTypeRegistryService
.ContentTypes
Expand All @@ -119,58 +110,23 @@ private IEnumerable<IContentType> GetExtensionContentTypes(string fileExtension,
}

private static bool CanDocumentHaveCss(string fileExtension, IEnumerable<IContentType> contentTypes) =>
fileExtension == VueExtension || contentTypes.Any(type => type.IsOfType("Vue")) || IsCssDocument(contentTypes);
fileExtension == VueExtension || contentTypes.Any(type => type.IsOfType(VueTypeName)) || IsCssDocument(contentTypes);

private static bool IsJavascriptDocument(string fileExtension, IEnumerable<IContentType> contentTypes) =>
JavascriptSupportedExtensions.Contains(fileExtension) ||
contentTypes.Any(type => type.IsOfType("JavaScript") ||
type.IsOfType("Vue"));
contentTypes.Any(type => type.IsOfType(JavascriptTypeName) ||
type.IsOfType(VueTypeName));

private static bool IsCFamilyDocument(IEnumerable<IContentType> contentTypes) =>
contentTypes.Any(type => type.IsOfType("C/C++"));
contentTypes.Any(type => type.IsOfType(CFamilyTypeName));

private static bool IsRoslynFamilyDocument(IEnumerable<IContentType> contentTypes) =>
contentTypes.Any(type => type.IsOfType("Roslyn Languages"));
contentTypes.Any(type => type.IsOfType(RoslynLanguagesTypeName));

private static bool IsTypeScriptDocument(IEnumerable<IContentType> contentTypes)
{
return contentTypes.Any(type => type.IsOfType("TypeScript"));
}
private static bool IsTypeScriptDocument(IEnumerable<IContentType> contentTypes) => contentTypes.Any(type => type.IsOfType(TypeScriptTypeName));

private static bool IsCssDocument(IEnumerable<IContentType> contentTypes) =>
contentTypes.Any(type => type.IsOfType(CSSTypeName) || type.IsOfType(SCSSTypeName) || type.IsOfType(LESSTypeName));

public AnalysisLanguage? GetAnalysisLanguageFromExtension(string fileExtension)
{
if (string.IsNullOrEmpty(fileExtension))
{
return null;
}

// ContentType for "js" is typescript we do manual check to be consistent with Detect method
if (JavascriptSupportedExtensions.Contains(fileExtension)) { return AnalysisLanguage.Javascript; }

var contentTypeName = fileExtensionRegistryService.GetContentTypeForExtension(fileExtension).TypeName;
switch (contentTypeName)
{
case TypeScriptTypeName:
return AnalysisLanguage.TypeScript;

case CFamilyTypeName:
return AnalysisLanguage.CFamily;

case CSharpTypeName:
case BasicTypeName:
return AnalysisLanguage.RoslynFamily;

case CSSTypeName:
case SCSSTypeName:
case LESSTypeName:
return AnalysisLanguage.CascadingStyleSheets;

default:
return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void NoListeners_NoError()
var logger = new TestLogger();
var testSubject = new AnalysisRequester(logger);

testSubject.RequestAnalysis();
testSubject.QueueAnalyzeOpenFiles();

logger.AssertNoOutputMessages();
}
Expand All @@ -44,7 +44,7 @@ public void OneListener_EventIsRaised()

bool eventRaised = false;
object actualSender = null;
AnalysisRequestEventArgs actualEventArgs = null;
EventArgs actualEventArgs = null;

testSubject.AnalysisRequested += (s, args) =>
{
Expand All @@ -57,13 +57,12 @@ public void OneListener_EventIsRaised()
};

// Act
testSubject.RequestAnalysis("file1", "c:\\aaa\\bbb.cs");
testSubject.QueueAnalyzeOpenFiles();

// Assert
eventRaised.Should().BeTrue();
actualSender.Should().Be(testSubject);
actualEventArgs.Should().NotBeNull();
actualEventArgs.FilePaths.Should().BeEquivalentTo("file1", "c:\\aaa\\bbb.cs");
}

[TestMethod]
Expand All @@ -75,7 +74,7 @@ public void NonCriticalErrorInListener_IsSuppressedAndLogged()
testSubject.AnalysisRequested += (s, e) => throw new ArgumentException("XXX yyy");

// Act
testSubject.RequestAnalysis();
testSubject.QueueAnalyzeOpenFiles();

// Assert
logger.AssertPartialOutputStringExists("XXX yyy");
Expand All @@ -89,7 +88,7 @@ public void CriticalErrorInListener_IsNotSuppressedORLogged()

testSubject.AnalysisRequested += (s, e) => throw new StackOverflowException("XXX overflow");

Action act = () => testSubject.RequestAnalysis();
Action act = () => testSubject.QueueAnalyzeOpenFiles();

// Act
act.Should().ThrowExactly<StackOverflowException>().And.Message.Should().Be("XXX overflow");
Expand Down
Loading