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

[Do not merge] Enable razor in nuget and Sonarlint #8660

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -67,6 +67,9 @@ public sealed class ParameterTypeShouldMatchRouteTypeConstraint : SonarDiagnosti
{
foreach (var property in GetPropertyTypeMismatches((INamedTypeSymbol)c.Symbol, cc.Compilation))
{

//System.Diagnostics.Debugger.Launch();

c.ReportIssue(Diagnostic.Create(Rule,
property.Type.GetLocation(),
property.ToAdditionalLocation(),
Expand Down
Expand Up @@ -133,6 +133,8 @@ public void RegisterNodeAction<TSyntaxKind>(GeneratedCodeRecognizer generatedCod
public void RegisterNodeActionInAllFiles<TSyntaxKind>(Action<SonarSyntaxNodeReportingContext> action, params TSyntaxKind[] syntaxKinds) where TSyntaxKind : struct =>
analysisContext.RegisterSyntaxNodeAction(c => action(new(this, c)), syntaxKinds);

// This function will always return true for non-Razor files.
// Otherwise, it returns true if the rule is not noisy on Razor files and not a test-only rule.
internal bool ShouldAnalyzeRazorFile(SyntaxTree sourceTree) =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the name is quite counter intuitive

!GeneratedCodeRecognizer.IsRazorGeneratedFile(sourceTree)
|| !SupportedDiagnostics.Any(x => (x.CustomTags.Count() == 1 && x.CustomTags.Contains(DiagnosticDescriptorFactory.TestSourceScopeTag))
Expand Down
Expand Up @@ -100,8 +100,7 @@ public SonarLintXmlReader SonarLintXml()
}

public bool IsRazorAnalysisEnabled() =>
ProjectConfiguration().ProjectType != ProjectType.Unknown
&& SonarLintXml().AnalyzeRazorCode(Compilation.Language);
SonarLintXml().AnalyzeRazorCode(Compilation.Language);

public bool IsTestProject()
{
Expand Down Expand Up @@ -167,6 +166,7 @@ private bool IsExcluded(SonarLintXmlReader sonarLintXml, string filePath)
private ImmutableHashSet<string> CreateUnchangedFilesHashSet() =>
ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, ProjectConfiguration().AnalysisConfig?.UnchangedFiles() ?? Array.Empty<string>());

// FIXME this needs to be updated as well
private static string MapFilePath(SyntaxTree tree) =>
// Currently only .razor file hashes are stored in the cache.
//
Expand Down
Expand Up @@ -26,11 +26,56 @@ public abstract class SonarReportingContextBase<TContext> : SonarAnalysisContext

protected SonarReportingContextBase(SonarAnalysisContext analysisContext, TContext context) : base(analysisContext, context) { }

private readonly HashSet<string> rulesEnabledForRazor = new()
{
/** Disabled rules, don't enable these
"S103",
"S104",
"S109",
"S113",
"S1147",
"S1192",
"S1451",*/
// blazor rules
"S6797",
"S6798",
"S6800",
"S6802",
"S6803",
// normal rules
"S2930",
"S3889",
"S3869",
"S2437",
"S1067",
"S3776"
};


protected void ReportIssueCore(Diagnostic diagnostic)
{
diagnostic = EnsureDiagnosticLocation(diagnostic);
if (!GeneratedCodeRecognizer.IsRazorGeneratedFile(diagnostic.Location.SourceTree) // In case of Razor generated content, we don't want to raise any issues
&& HasMatchingScope(diagnostic.Descriptor)
// FIXME: this logic works for VS 17.9 and newer
// for older versions, we need to
// `diagnostic = EnsureDiagnosticLocation(diagnostic);`
// and return if the diagnostic is still on a generated file
// `!GeneratedCodeRecognizer.IsRazorGeneratedFile(diagnostic.Location.SourceTree)`
if (!diagnostic.Location.GetMappedLineSpan().HasMappedPath)
{
return;
}
if (GeneratedCodeRecognizer.IsRazorGeneratedFile(diagnostic.Location.SourceTree)
&& (!rulesEnabledForRazor.Contains(diagnostic.Id) ||
(diagnostic.Descriptor.CustomTags.Count() == 1 && diagnostic.Descriptor.CustomTags.Contains(DiagnosticDescriptorFactory.TestSourceScopeTag))))
{
return;
}
// don't report twice

// Razor generated issues with mapped paths will be handled by the compiler in VS 17.9 and newer
// https://github.com/dotnet/razor/issues/9308
// https://github.com/dotnet/roslyn/issues/71449
// https://github.com/dotnet/roslyn/pull/71454
if (HasMatchingScope(diagnostic.Descriptor)
&& SonarAnalysisContext.LegacyIsRegisteredActionEnabled(diagnostic.Descriptor, diagnostic.Location?.SourceTree))
{
var reportingContext = CreateReportingContext(diagnostic);
Expand Down
Expand Up @@ -19,6 +19,7 @@
*/

using System.IO;
using System.Text.RegularExpressions;

namespace SonarAnalyzer.Helpers
{
Expand Down Expand Up @@ -61,15 +62,20 @@ public abstract class GeneratedCodeRecognizer
public static bool IsRazorGeneratedFile(SyntaxTree tree) =>
tree is not null && (IsRazor(tree) || IsCshtml(tree));

// match also ViewContact.razor.hyYZFFeGwGnGotaj.ide.g.cs
// but it can also be just ViewContact.razor.ide.g.cs
static Regex regex = new Regex(".*razor(\\.[a-zA-Z0-9]*)?(\\.ide)?\\.g\\.cs$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

public static bool IsRazor(SyntaxTree tree) =>
// razor.ide.g.cs is the extension for razor-generated files in the context of design-time builds.
// However, it is not considered here because of https://github.com/dotnet/razor/issues/9108
tree.FilePath.EndsWith("razor.g.cs", StringComparison.OrdinalIgnoreCase);
regex.IsMatch(tree.FilePath);
// tree.FilePath.EndsWith("razor.g.cs", StringComparison.OrdinalIgnoreCase)
// || tree.FilePath.EndsWith("razor.ide.g.cs", StringComparison.OrdinalIgnoreCase);

// FIXME how can I test if the new behavior for CSHTML is the same with Razor
// ViewContact.cshtml.hyYZFFeGwGnGotaj.ide.g.cs
public static bool IsCshtml(SyntaxTree tree) =>
// cshtml.ide.g.cs is the extension for razor-generated files in the context of design-time builds.
// However, it is not considered here because of https://github.com/dotnet/razor/issues/9108
tree.FilePath.EndsWith("cshtml.g.cs", StringComparison.OrdinalIgnoreCase);
tree.FilePath.EndsWith("cshtml.g.cs", StringComparison.OrdinalIgnoreCase)
|| tree.FilePath.EndsWith("cshtml.ide.g.cs", StringComparison.OrdinalIgnoreCase);

private bool HasGeneratedCommentOrAttribute(SyntaxTree tree)
{
Expand Down
Expand Up @@ -46,6 +46,7 @@ public void ReportUnusedVariables(SonarCodeBlockReportingContext c, DiagnosticDe
{
foreach (var unused in declaredLocals.Except(usedLocals))
{
//System.Diagnostics.Debugger.Launch();
c.ReportIssue(Diagnostic.Create(rule, unused.Locations.First(), unused.Name));
}
}
Expand Down
Expand Up @@ -63,6 +63,8 @@ public void IsGenerated_NonGeneratedPath_ReturnsTrue()
[DataRow("C:\\SonarSource\\SomeFile_razor.g.cs")]
[DataRow("C:\\SonarSource\\SomeFile_cshtml.g.cs")]
[DataRow("C:\\SonarSource\\SomeFile_RAZOR.g.cS")]
[DataRow("C:\\SonarSource\\SomeFile_razor.ide.g.cs")]
[DataRow("C:\\SonarSource\\SomeFile_cshtml.ide.g.cs")]
public void IsRazorGeneratedFile_RazorGeneratedFiles_ReturnsTrue(string path)
{
// GetRoot() cannot be mocked - not virtual, so we use Loose behaviour to return null Root
Expand All @@ -77,8 +79,6 @@ public void IsRazorGeneratedFile_RazorGeneratedFiles_ReturnsTrue(string path)
[DataRow("C:\\SonarSource\\SomeFile_razor.g.cs.randomEnding")]
[DataRow("C:\\SonarSource\\SomeFile_cshtml.g.cs.randomEnding")]
[DataRow("C:\\SonarSource\\SomeFile_razor.g.ß")]
[DataRow("C:\\SonarSource\\SomeFile_razor.ide.g.cs")] // Not considered razor file because of https://github.com/dotnet/razor/issues/9108
[DataRow("C:\\SonarSource\\SomeFile_cshtml.ide.g.cs")] // Not considered razor file because of https://github.com/dotnet/razor/issues/9108
public void IsRazorGeneratedFile_NonRazorGeneratedFiles_ReturnsFalse(string path)
{
var syntaxTree = new Mock<SyntaxTree>(MockBehavior.Loose);
Expand Down
Expand Up @@ -182,11 +182,11 @@ public class Sample
[DataTestMethod]
[DataRow("Dummy.razor")]
[DataRow("Dummy.cshtml")]
public void Verify_RazorAnalysisInSLAndNugetContext_DoesNotRaise(string path) =>
public void Verify_RazorAnalysisInSLAndNugetContext(string path) =>
DummyWithLocation
.AddPaths(path)
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Unknown))
.VerifyNoIssueReported();
.Verify();

[DataTestMethod]
[DataRow("net6.0")]
Expand Down