Skip to content

Commit

Permalink
Add support for muting file-level C#&VB.NET issues
Browse files Browse the repository at this point in the history
  • Loading branch information
georgii-borovinskikh-sonarsource committed Dec 8, 2023
1 parent d9928c9 commit d2618fb
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 26 deletions.
31 changes: 31 additions & 0 deletions src/ConnectedMode.UnitTests/IssueMatcherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,37 @@ public void IsMatch_FileLevelIssue(string serverRuleId, int? serverIssueLine,
CreateTestSubject().IsLikelyMatch(issueToMatch, serverIssue).Should().Be(expectedResult);
}

[DataTestMethod]
[DataRow("CorrectRuleId", null, null, true)] // exact matches
[DataRow("CorrectRuleId", null, "hash", true)] // hash should be ignored for file-level issues
[DataRow("WrongRuleId", null, null, false)] // wrong rule
[DataRow("CorrectRuleId", 20, "hash", true)] // roslyn issue is not actually a file issue, so it should match
[DataRow("CorrectRuleId", 1, null, true)] // roslyn issue is not actually a file issue, so it should match
[DataRow("CorrectRuleId", 999, null, false)] // not a file issue - should not match a file issue, even though the hash is the same
public void IsMatch_PotentialRoslynFileLevelIssue_ServerIssueVariation(string serverRuleId, int? serverIssueLine,
string serverHash, bool expectedResult)
{
// File issues for roslyn have line and column number equal to 1
var issueToMatch = new FilterableRoslynIssue("CorrectRuleId", null, 1, 1);
issueToMatch.SetLineHash("hash"); // hash is always calculated since we don't know if it's a file issue or not
var serverIssue = CreateServerIssue(serverRuleId, serverIssueLine, serverHash);

CreateTestSubject().IsLikelyMatch(issueToMatch, serverIssue).Should().Be(expectedResult);
}

[DataTestMethod]
[DataRow("CorrectRuleId", 1, 1, true)] // potential file issue matches server file issue
[DataRow("CorrectRuleId", 1, 2, false)] // not a file issue
[DataRow("WrongRuleId", 1, 1, false)] // wrong rule
public void IsMatch_PotentialRoslynFileLevelIssue_RoslynIssueVariation(string roslynIssueId, int startLine, int startColumn, bool expectedResult)
{
var issueToMatch = new FilterableRoslynIssue(roslynIssueId, null, startLine, startColumn);
issueToMatch.SetLineHash("hash"); // hash is always calculated since we don't know if it's a file issue or not
var serverIssue = CreateServerIssue("CorrectRuleId", null, null);

CreateTestSubject().IsLikelyMatch(issueToMatch, serverIssue).Should().Be(expectedResult);
}

[TestMethod]
// Module-level issues i.e. no file
[DataRow(null, null, true)]
Expand Down
15 changes: 11 additions & 4 deletions src/ConnectedMode/IssueMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,20 @@ private static bool IsMatch(IFilterableIssue issue, SonarQubeIssue serverIssue,
return false;
}

if (!issue.StartLine.HasValue) // i.e. file-level issue
// file level issue
if (serverIssue.TextRange == null)
{
return serverIssue.TextRange == null;
return !issue.StartLine.HasValue
|| (issue is IFilterableRoslynIssue roslynIssue // We don't know the end of the issue location, so this is our best guess.
&& roslynIssue.RoslynStartLine == 1
&& roslynIssue.RoslynStartColumn == 1);// This check relies on the fact that a roslyn file-level issue
// always starts at the beginning of the file
// and the fact that the rule can't be both file-level and not.
// See SuppressionChecker.IsSameLine for an example of a solution w/o false-positives
}

// Non-file level issue

return issue.StartLine == serverIssue.TextRange?.StartLine || CompareHash(issue, serverIssue);
}

Expand Down
11 changes: 8 additions & 3 deletions src/Core/Suppressions/FilterableRoslynIssue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,25 @@ namespace SonarLint.VisualStudio.Core.Suppressions
public interface IFilterableRoslynIssue : IFilterableIssue
{
void SetLineHash(string lineHash);
int RoslynStartLine { get; }
int RoslynStartColumn { get; }
}

public class FilterableRoslynIssue : IFilterableRoslynIssue
{
public FilterableRoslynIssue(string ruleId, string filePath, int? startLine)
public FilterableRoslynIssue(string ruleId, string filePath, int startLine, int startColumn)
{
RuleId = ruleId;
FilePath = filePath;
StartLine = startLine;
RoslynStartLine = startLine;
RoslynStartColumn = startColumn;
}

public string RuleId { get; }
public string FilePath { get; }
public int? StartLine { get; }
public int? StartLine => RoslynStartLine;
public int RoslynStartLine { get; }
public int RoslynStartColumn { get; }
public string LineHash { get; private set; }

public void SetLineHash(string lineHash)
Expand Down
52 changes: 36 additions & 16 deletions src/Infrastructure.VS.UnitTests/ErrorListHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,15 @@ public void TryGetRoslynIssueFromSelectedRow_SingleRoslynIssue_IssueReturned()
{
var path = "filepath";
var line = 12;
var column = 101;
var errorCode = "javascript:S333";
var issueHandle = CreateIssueHandle(111, new Dictionary<string, object>
{
{ StandardTableKeyNames.BuildTool, "SonarLint" },
{ StandardTableKeyNames.ErrorCode, errorCode},
{ StandardTableKeyNames.DocumentName, path },
{ StandardTableKeyNames.Line, line }
{ StandardTableKeyNames.Line, line },
{ StandardTableKeyNames.Column, column }
});
var errorList = CreateErrorList(issueHandle);
var serviceProvider = CreateServiceOperation(errorList);
Expand All @@ -143,21 +145,21 @@ public void TryGetRoslynIssueFromSelectedRow_SingleRoslynIssue_IssueReturned()
issue.RuleId.Should().BeSameAs(errorCode);
issue.FilePath.Should().BeSameAs(path);
issue.StartLine.Should().Be(line + 1);
issue.RoslynStartLine.Should().Be(line + 1);
issue.RoslynStartColumn.Should().Be(column + 1);
issue.LineHash.Should().BeNull();
}

[TestMethod]
public void TryGetRoslynIssueFromSelectedRow_NonSonarIssue_NothingReturned()
{
var path = "filepath";
var line = 12;
var errorCode = "javascript:S333";
var issueHandle = CreateIssueHandle(111, new Dictionary<string, object>
{
{ StandardTableKeyNames.BuildTool, "Not SonarLint" },
{ StandardTableKeyNames.ErrorCode, errorCode},
{ StandardTableKeyNames.DocumentName, path },
{ StandardTableKeyNames.Line, line }
{ StandardTableKeyNames.ErrorCode, "javascript:S333"},
{ StandardTableKeyNames.DocumentName, "filepath" },
{ StandardTableKeyNames.Line, 1 },
{ StandardTableKeyNames.Column, 2 }
});
var errorList = CreateErrorList(issueHandle);
var serviceProvider = CreateServiceOperation(errorList);
Expand All @@ -171,13 +173,13 @@ public void TryGetRoslynIssueFromSelectedRow_NonSonarIssue_NothingReturned()
[TestMethod]
public void TryGetRoslynIssueFromSelectedRow_NoFilePath_NothingReturned()
{
var line = 12;
var errorCode = "javascript:S333";

var issueHandle = CreateIssueHandle(111, new Dictionary<string, object>
{
{ StandardTableKeyNames.BuildTool, "SonarLint" },
{ StandardTableKeyNames.ErrorCode, errorCode},
{ StandardTableKeyNames.Line, line }
{ StandardTableKeyNames.ErrorCode, "javascript:S333"},
{ StandardTableKeyNames.Line, 1 },
{ StandardTableKeyNames.Column, 2 }
});
var errorList = CreateErrorList(issueHandle);
var serviceProvider = CreateServiceOperation(errorList);
Expand All @@ -191,13 +193,12 @@ public void TryGetRoslynIssueFromSelectedRow_NoFilePath_NothingReturned()
[TestMethod]
public void TryGetRoslynIssueFromSelectedRow_NoStartLine_NothingReturned()
{
var path = "filepath";
var errorCode = "javascript:S333";
var issueHandle = CreateIssueHandle(111, new Dictionary<string, object>
{
{ StandardTableKeyNames.BuildTool, "SonarLint" },
{ StandardTableKeyNames.ErrorCode, errorCode},
{ StandardTableKeyNames.DocumentName, path },
{ StandardTableKeyNames.ErrorCode, "javascript:S333"},
{ StandardTableKeyNames.DocumentName, "filepath" },
{ StandardTableKeyNames.Column, 2 },
});
var errorList = CreateErrorList(issueHandle);
var serviceProvider = CreateServiceOperation(errorList);
Expand All @@ -207,7 +208,26 @@ public void TryGetRoslynIssueFromSelectedRow_NoStartLine_NothingReturned()

result.Should().BeFalse();
}


[TestMethod]
public void TryGetRoslynIssueFromSelectedRow_NoStartColumn_NothingReturned()
{
var issueHandle = CreateIssueHandle(111, new Dictionary<string, object>
{
{ StandardTableKeyNames.BuildTool, "SonarLint" },
{ StandardTableKeyNames.ErrorCode, "javascript:S333"},
{ StandardTableKeyNames.DocumentName, "filepath" },
{ StandardTableKeyNames.Line, 1 },
});
var errorList = CreateErrorList(issueHandle);
var serviceProvider = CreateServiceOperation(errorList);

var testSubject = new ErrorListHelper(serviceProvider);
bool result = testSubject.TryGetRoslynIssueFromSelectedRow(out _);

result.Should().BeFalse();
}

[TestMethod]
[DataRow("S666", "csharpsquid", "S666", "SonarAnalyzer.CSharp")]
[DataRow("S666", "vbnet", "S666", "SonarAnalyzer.VisualBasic")]
Expand Down
6 changes: 3 additions & 3 deletions src/Infrastructure.VS/ErrorListHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ public bool TryGetRoslynIssueFromSelectedRow(out IFilterableRoslynIssue filterab
if (TryGetSelectedSnapshotAndIndex(errorList, out var snapshot, out var index)
&& (errorCode = FindErrorCodeForEntry(snapshot, index)) != null
&& TryGetValue(snapshot, index, StandardTableKeyNames.DocumentName, out string filePath)
&& TryGetValue(snapshot, index, StandardTableKeyNames.Line, out int line))
&& TryGetValue(snapshot, index, StandardTableKeyNames.Line, out int line)
&& TryGetValue(snapshot, index, StandardTableKeyNames.Column, out int column))
{
// todo support file level issues https://github.com/SonarSource/sonarlint-visualstudio/issues/5094
outIssue = new FilterableRoslynIssue(errorCode, filePath, line + 1 /* error list issues are 0-based and we use 1-based line numbers */);
outIssue = new FilterableRoslynIssue(errorCode, filePath, line + 1, column + 1 /* error list issues are 0-based and we use 1-based line & column numbers */);
}

return outIssue != null;
Expand Down

0 comments on commit d2618fb

Please sign in to comment.