Skip to content

Commit

Permalink
Improved algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
csaba-sagi-sonarsource authored and antonioaversa committed Sep 18, 2023
1 parent 8fd808c commit bb11351
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public class SymbolReferenceAnalyzer : SymbolReferenceAnalyzerBase<SyntaxKind>
protected override SyntaxNode GetBindableParent(SyntaxToken token) =>
token.GetBindableParent();

protected override ImmutableSortedSet<LineDirectiveEntry> CalculateLineDirectiveMap(SyntaxTree syntaxTree) =>
syntaxTree.GetRoot().DescendantNodes(_ => true, true).Where(x => x.IsKind(SyntaxKind.LineDirectiveTrivia)
|| x.IsKind(SyntaxKindEx.LineSpanDirectiveTrivia))
.Select(x => new LineDirectiveEntry(x.GetLocation().GetLineSpan().StartLinePosition.Line, x)).ToImmutableSortedSet();

protected override ReferenceInfo[] CreateDeclarationReferenceInfo(SyntaxNode node, SemanticModel model) =>
node switch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,16 @@ public class TokenTypeAnalyzer : TokenTypeAnalyzerBase<SyntaxKind>
{
protected override ILanguageFacade<SyntaxKind> Language { get; } = CSharpFacade.Instance;

protected override TokenClassifierBase GetTokenClassifier(SemanticModel semanticModel, bool skipIdentifierTokens, string filePath) =>
new TokenClassifier(semanticModel, skipIdentifierTokens, filePath);
protected override TokenClassifierBase GetTokenClassifier(SemanticModel semanticModel, bool skipIdentifierTokens, string filePath, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap) =>
new TokenClassifier(semanticModel, skipIdentifierTokens, filePath, lineDirectiveMap);

protected override TriviaClassifierBase GetTriviaClassifier(string filePath) =>
new TriviaClassifier(filePath);
protected override TriviaClassifierBase GetTriviaClassifier(string filePath, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap) =>
new TriviaClassifier(filePath, lineDirectiveMap);

protected override ImmutableSortedSet<LineDirectiveEntry> CalculateLineDirectiveMap(SyntaxTree syntaxTree) =>
syntaxTree.GetRoot().DescendantNodes(_ => true, true).Where(x => x.IsKind(SyntaxKind.LineDirectiveTrivia)
|| x.IsKind(SyntaxKindEx.LineSpanDirectiveTrivia))
.Select(x => new LineDirectiveEntry(x.GetLocation().GetLineSpan().StartLinePosition.Line, x)).ToImmutableSortedSet();

internal sealed class TokenClassifier : TokenClassifierBase
{
Expand All @@ -54,7 +59,8 @@ internal sealed class TokenClassifier : TokenClassifierBase
SyntaxKindEx.InterpolatedRawStringEndToken,
};

public TokenClassifier(SemanticModel semanticModel, bool skipIdentifiers, string filePath) : base(semanticModel, skipIdentifiers, filePath) { }
public TokenClassifier(SemanticModel semanticModel, bool skipIdentifiers, string filePath, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap)
: base(semanticModel, skipIdentifiers, filePath, lineDirectiveMap) { }

protected override SyntaxNode GetBindableParent(SyntaxToken token) =>
token.GetBindableParent();
Expand Down Expand Up @@ -343,7 +349,7 @@ internal sealed class TriviaClassifier : TriviaClassifierBase
SyntaxKind.MultiLineDocumentationCommentTrivia,
};

public TriviaClassifier(string filePath) : base(filePath) { }
public TriviaClassifier(string filePath, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap) : base(filePath, lineDirectiveMap) { }

protected override bool IsRegularComment(SyntaxTrivia trivia) =>
trivia.IsAnyKind(RegularCommentToken);
Expand Down
89 changes: 19 additions & 70 deletions analyzers/src/SonarAnalyzer.Common/Extensions/LocationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,88 +18,37 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using Microsoft.CodeAnalysis.CSharp;
using SonarAnalyzer.Rules;

namespace SonarAnalyzer.Extensions;

public static class LocationExtensions
{
public static FileLinePositionSpan GetMappedLineSpanIfAvailable(this Location location) =>
GeneratedCodeRecognizer.IsRazorGeneratedFile(location.SourceTree) ? location.GetMappedLineSpan() : location.GetLineSpan();

public static FileLinePositionSpan GetMappedLineSpanIfAvailable(this Location location, SyntaxToken token)
public static FileLinePositionSpan GetMappedLineSpanIfAvailable(this Location location, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap)
{
var node = token.Parent;
if (GeneratedCodeRecognizer.IsRazorGeneratedFile(location.SourceTree))
if (GeneratedCodeRecognizer.IsRazorGeneratedFile(location.SourceTree)
&& !lineDirectiveMap.IsEmpty)
{
var mappedLocation = location.GetMappedLineSpan();
if (mappedLocation.HasMappedPath)
var unmappedLocation = location.GetLineSpan().StartLinePosition.Line;
var lineSpanIndex = -1;
for (var i = 0; i < lineDirectiveMap.Count; i++)
{
var lineDirective = FindLineDirecitive(node);
if (LineSpanDirectiveTriviaSyntaxWrapper.IsInstance(lineDirective)
&& (LineSpanDirectiveTriviaSyntaxWrapper)lineDirective is var lineSpanDirective
&& lineSpanDirective.CharacterOffset.ValueText is var stringValue
&& int.TryParse(stringValue, out var numericValue)
&& numericValue >= location.GetLineSpan().Span.End.Character)
if (lineDirectiveMap[i].LineNumber > unmappedLocation)
{
return location.GetLineSpan();
lineSpanIndex = i - 1;
break;
}
}
return mappedLocation;
}
return location.GetLineSpan();
}

private static SyntaxNode FindLineDirecitive(SyntaxNode node)
{
while (node != null)
{
if (LineDirective(node) is { } lineDirective)
{
return lineDirective;
}
var directive = FindLineDirectiveOnSameLevel(node);

if (directive != null)
{
return directive;
}

node = node.Parent;
}
return null;
}

private static SyntaxNode FindLineDirectiveOnSameLevel(SyntaxNode node)
{
var childNodes = node.Parent.ChildNodes().ToArray();
var index = -1;
for (var i = 0; i < childNodes.Count(); i++)
{
if (childNodes[i] == node)
{
index = i;
break;
}
}

if (index == -1)
{
return null;
}

for (var j = index; j >= 0; j--)
{
if (LineDirective(childNodes[j]) is { } lineDirective)
{
return lineDirective;
}
return lineSpanIndex != -1
&& LineSpanDirectiveTriviaSyntaxWrapper.IsInstance(lineDirectiveMap[lineSpanIndex].LineDirective)
&& (LineSpanDirectiveTriviaSyntaxWrapper)lineDirectiveMap[lineSpanIndex].LineDirective is var lineSpanDirective
&& lineSpanDirective.CharacterOffset.ValueText is var stringValue
&& int.TryParse(stringValue, out var numericValue)
&& numericValue >= location.GetLineSpan().Span.End.Character
? location.GetLineSpan()
: location.GetMappedLineSpan();
}

return null;
return location.GetLineSpan();
}

private static SyntaxNode LineDirective(SyntaxNode node) =>
node.DescendantNodes(_ => true, true).FirstOrDefault(x => x.IsKind(SyntaxKind.LineDirectiveTrivia)
|| x.IsKind(SyntaxKindEx.LineSpanDirectiveTrivia));
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public abstract class CopyPasteTokenAnalyzerBase<TSyntaxKind> : UtilityAnalyzerB
!GeneratedCodeRecognizer.IsRazorGeneratedFile(tree)
&& base.ShouldGenerateMetrics(tree, compilation);

protected sealed override CopyPasteTokenInfo CreateMessage(SyntaxTree tree, SemanticModel model)
protected sealed override CopyPasteTokenInfo CreateMessage(SyntaxTree tree, SemanticModel model, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap)
{
var cpdTokenInfo = new CopyPasteTokenInfo { FilePath = tree.FilePath };
foreach (var token in tree.GetRoot().DescendantTokens(n => !IsUsingDirective(n)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@ public abstract class FileMetadataAnalyzerBase<TSyntaxKind> : UtilityAnalyzerBas
protected override bool AnalyzeGeneratedCode => true;

protected FileMetadataAnalyzerBase() : base(DiagnosticId, Title) { }

protected override bool ShouldGenerateMetrics(SyntaxTree tree, Compilation compilation) =>
!GeneratedCodeRecognizer.IsRazorGeneratedFile(tree)
&& base.ShouldGenerateMetrics(tree, compilation);

protected sealed override FileMetadataInfo CreateMessage(SyntaxTree tree, SemanticModel model) =>
new()
protected sealed override FileMetadataInfo CreateMessage(SyntaxTree tree, SemanticModel model, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap) =>
new FileMetadataInfo
{
FilePath = tree.FilePath,
IsGenerated = Language.GeneratedCodeRecognizer.IsGenerated(tree),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SonarAnalyzer for .NET
* Copyright (C) 2015-2023 SonarSource SA
* mailto: contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarAnalyzer.Rules
{
public readonly struct LineDirectiveEntry : IComparable<LineDirectiveEntry>
{
public readonly int LineNumber;
public readonly SyntaxNode LineDirective;

public LineDirectiveEntry(int lineNumber)
{
LineNumber = lineNumber;
LineDirective = null;
}

public LineDirectiveEntry(int lineNumber, SyntaxNode lineDirective)
{
LineNumber = lineNumber;
LineDirective = lineDirective;
}

public int CompareTo(LineDirectiveEntry other) =>
LineNumber.CompareTo(other.LineNumber);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ public abstract class LogAnalyzerBase<TSyntaxKind> : UtilityAnalyzerBase<TSyntax
new LogInfo { Severity = LogSeverity.Info, Text = "Concurrent execution: " + (IsConcurrentExecutionEnabled() ? "enabled" : "disabled") }
};

protected sealed override LogInfo CreateMessage(SyntaxTree tree, SemanticModel model) =>
protected sealed override LogInfo CreateMessage(SyntaxTree tree, SemanticModel model, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap) =>
tree.IsGenerated(Language.GeneratedCodeRecognizer, model.Compilation)
? CreateMessage(tree)
? new LogInfo { Severity = LogSeverity.Debug, Text = $"File '{syntaxTree.FilePath}' was recognized as generated" }
: null;

private static LogInfo CreateMessage(SyntaxTree tree) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public abstract class MetricsAnalyzerBase<TSyntaxKind> : UtilityAnalyzerBase<TSy

protected MetricsAnalyzerBase() : base(DiagnosticId, Title) { }

protected sealed override MetricsInfo CreateMessage(SyntaxTree tree, SemanticModel model)
protected sealed override MetricsInfo CreateMessage(SyntaxTree tree, SemanticModel model, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap)
{
var metrics = GetMetrics(tree, model);
var complexity = metrics.Complexity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ public abstract class SymbolReferenceAnalyzerBase<TSyntaxKind> : UtilityAnalyzer

protected SymbolReferenceAnalyzerBase() : base(DiagnosticId, Title) { }

protected sealed override SymbolReferenceInfo CreateMessage(SyntaxTree tree, SemanticModel model)
protected sealed override SymbolReferenceInfo CreateMessage(SyntaxTree tree, SemanticModel model, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap)
{
var filePath = GetFilePath(tree);
var symbolReferenceInfo = new SymbolReferenceInfo { FilePath = filePath };
var references = GetReferences(tree.GetRoot(), model);
foreach (var symbol in references.Keys)
{
if (GetSymbolReference(references[symbol], filePath) is { } reference)
if (GetSymbolReference(references[symbol], filePath, lineDirectiveMap) is { } reference)
{
symbolReferenceInfo.Reference.Add(reference);
}
Expand Down Expand Up @@ -111,9 +111,9 @@ protected sealed override SymbolReferenceInfo CreateMessage(SyntaxTree tree, Sem
var symbol => symbol
};

private static SymbolReferenceInfo.Types.SymbolReference GetSymbolReference(IReadOnlyList<ReferenceInfo> references, string filePath)
private static SymbolReferenceInfo.Types.SymbolReference GetSymbolReference(IReadOnlyList<ReferenceInfo> references, string filePath, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap)
{
var declarationSpan = GetDeclarationSpan(references, filePath);
var declarationSpan = GetDeclarationSpan(references, filePath, lineDirectiveMap);
if (!declarationSpan.HasValue)
{
return null;
Expand All @@ -124,7 +124,7 @@ private static SymbolReferenceInfo.Types.SymbolReference GetSymbolReference(IRea
{
var reference = references[i];
if (!reference.IsDeclaration
&& reference.Identifier.GetLocation().GetMappedLineSpanIfAvailable(reference.Identifier) is var mappedLineSpan
&& reference.Identifier.GetLocation().GetMappedLineSpanIfAvailable(lineDirectiveMap) is var mappedLineSpan
&& string.Equals(mappedLineSpan.Path, filePath, StringComparison.OrdinalIgnoreCase))
{
symbolReference.Reference.Add(GetTextRange(mappedLineSpan));
Expand All @@ -133,12 +133,12 @@ private static SymbolReferenceInfo.Types.SymbolReference GetSymbolReference(IRea
return symbolReference;
}

private static FileLinePositionSpan? GetDeclarationSpan(IReadOnlyList<ReferenceInfo> references, string filePath)
private static FileLinePositionSpan? GetDeclarationSpan(IReadOnlyList<ReferenceInfo> references, string filePath, ImmutableSortedSet<LineDirectiveEntry> lineDirectiveMap)
{
for (var i = 0; i < references.Count; i++)
{
if (references[i].IsDeclaration
&& references[i].Identifier.GetLocation().GetMappedLineSpanIfAvailable(references[i].Identifier) is var mappedLineSpan
&& references[i].Identifier.GetLocation().GetMappedLineSpanIfAvailable(lineDirectiveMap) is var mappedLineSpan
&& string.Equals(mappedLineSpan.Path, filePath, StringComparison.OrdinalIgnoreCase))
{
return mappedLineSpan;
Expand Down

0 comments on commit bb11351

Please sign in to comment.