Skip to content

Commit

Permalink
Merge pull request #2032 from sharwell/fix-2029
Browse files Browse the repository at this point in the history
Implement SA0002 (InvalidSettingsFile)
  • Loading branch information
sharwell committed Jan 12, 2016
2 parents 33d6540 + 41b5dc3 commit bd2f647
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ namespace StyleCop.Analyzers.Settings
[Shared]
internal class SettingsFileCodeFixProvider : CodeFixProvider
{
private const string StyleCopSettingsFileName = "stylecop.json";
private const string DefaultSettingsFileContent = @"{
internal const string DefaultSettingsFileContent = @"{
// ACTION REQUIRED: This file was automatically added to your project, but it
// will not take effect until additional steps are taken to enable it. See the
// following page for additional information:
Expand All @@ -40,6 +39,8 @@ internal class SettingsFileCodeFixProvider : CodeFixProvider
}
";

private const string StyleCopSettingsFileName = "stylecop.json";

/// <inheritdoc/>
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public async Task TestDisabledDocumentationModesAsync(DocumentationMode document
}
";

DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(null, 0, 0);
// This diagnostic is reported without a location
DiagnosticResult expected = this.CSharpDiagnostic();

this.documentationMode = documentationMode;
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

namespace StyleCop.Analyzers.Test.SpecialRules
{
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Analyzers.Settings;
using Analyzers.SpecialRules;
using Microsoft.CodeAnalysis.Diagnostics;
using TestHelper;
using Xunit;

/// <summary>
/// Unit tests for <see cref="SA0002InvalidSettingsFile"/>.
/// </summary>
public class SA0002UnitTests : DiagnosticVerifier
{
private const string TestCode = @"
namespace NamespaceName { }
";

private string settings;

[Fact]
public async Task TestMissingSettingsAsync()
{
await this.VerifyCSharpDiagnosticAsync(TestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestValidSettingsAsync()
{
this.settings = SettingsFileCodeFixProvider.DefaultSettingsFileContent;

await this.VerifyCSharpDiagnosticAsync(TestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestInvalidSettingsAsync()
{
this.settings = @"
{
""$schema"": ""https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json""
""settings"": {
""documentationRules"": {
""companyName"": ""ACME, Inc"",
""copyrightText"": ""Copyright 2015 {companyName}. All rights reserved.""
}
}
}
";

// This diagnostic is reported without a location
DiagnosticResult expected = this.CSharpDiagnostic();

await this.VerifyCSharpDiagnosticAsync(TestCode, expected, CancellationToken.None).ConfigureAwait(false);
}

/// <inheritdoc/>
protected override string GetSettings()
{
return this.settings;
}

/// <inheritdoc/>
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
{
yield return new SA0002InvalidSettingsFile();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@
<Compile Include="SpacingRules\SA1027UnitTests.cs" />
<Compile Include="SpacingRules\SA1028UnitTests.cs" />
<Compile Include="SpecialRules\SA0001UnitTests.cs" />
<Compile Include="SpecialRules\SA0002UnitTests.cs" />
<Compile Include="Verifiers\CodeFixVerifier.cs" />
<Compile Include="Verifiers\DiagnosticVerifier.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,26 @@ private static string FormatDiagnostics(ImmutableArray<DiagnosticAnalyzer> analy
return builder.ToString();
}

private static bool IsSubjectToExclusion(DiagnosticResult result)
{
if (result.Id.StartsWith("CS", StringComparison.Ordinal))
{
return false;
}

if (result.Id.StartsWith("AD", StringComparison.Ordinal))
{
return false;
}

if (result.Locations.Length == 0)
{
return false;
}

return true;
}

/// <summary>
/// General method that gets a collection of actual <see cref="Diagnostic"/>s found in the source after the
/// analyzer is run, then verifies each of them.
Expand All @@ -346,12 +366,13 @@ private async Task VerifyDiagnosticsAsync(string[] sources, string language, Imm
if (filenames == null)
{
// Also check if the analyzer honors exclusions
if (expected.Any(x => x.Id.StartsWith("SA", StringComparison.Ordinal) || x.Id.StartsWith("SX", StringComparison.Ordinal)))
if (expected.Any(IsSubjectToExclusion))
{
// We want to look at non-stylecop diagnostics only. We also insert a new line at the beginning
// so we have to move all diagnostic location down by one line
// Diagnostics reported by the compiler and analyzer diagnostics which don't have a location will
// still be reported. We also insert a new line at the beginning so we have to move all diagnostic
// locations which have a specific position down by one line.
var expectedResults = expected
.Where(x => !x.Id.StartsWith("SA", StringComparison.Ordinal) && !x.Id.StartsWith("SX", StringComparison.Ordinal))
.Where(x => !IsSubjectToExclusion(x))
.Select(x => x.WithLineOffset(1))
.ToArray();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

namespace StyleCop.Analyzers
{
using Newtonsoft.Json;
using StyleCop.Analyzers.Settings.ObjectModel;

/// <summary>
/// Defines the behavior of various <see cref="SettingsHelper"/> methods in the event of a deserialization error.
/// </summary>
internal enum DeserializationFailureBehavior
{
/// <summary>
/// When deserialization fails, return a default <see cref="StyleCopSettings"/> instance.
/// </summary>
ReturnDefaultSettings,

/// <summary>
/// When deserialization fails, throw a <see cref="JsonException"/> containing details about the error.
/// </summary>
ThrowException
}
}
27 changes: 24 additions & 3 deletions StyleCop.Analyzers/StyleCop.Analyzers/Settings/SettingsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ static SettingsHelper()
/// <summary>
/// Gets the StyleCop settings.
/// </summary>
/// <remarks>
/// <para>If a <see cref="JsonException"/> occurs while deserializing the settings file, a default settings
/// instance is returned.</para>
/// </remarks>
/// <param name="context">The context that will be used to determine the StyleCop settings.</param>
/// <param name="cancellationToken">The cancellation token that the operation will observe.</param>
/// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns>
Expand All @@ -52,15 +56,32 @@ internal static StyleCopSettings GetStyleCopSettings(this SyntaxTreeAnalysisCont
/// <summary>
/// Gets the StyleCop settings.
/// </summary>
/// <remarks>
/// <para>If a <see cref="JsonException"/> occurs while deserializing the settings file, a default settings
/// instance is returned.</para>
/// </remarks>
/// <param name="options">The analyzer options that will be used to determine the StyleCop settings.</param>
/// <param name="cancellationToken">The cancellation token that the operation will observe.</param>
/// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns>
internal static StyleCopSettings GetStyleCopSettings(this AnalyzerOptions options, CancellationToken cancellationToken)
{
return GetStyleCopSettings(options != null ? options.AdditionalFiles : ImmutableArray.Create<AdditionalText>(), cancellationToken);
return GetStyleCopSettings(options, DeserializationFailureBehavior.ReturnDefaultSettings, cancellationToken);
}

private static StyleCopSettings GetStyleCopSettings(ImmutableArray<AdditionalText> additionalFiles, CancellationToken cancellationToken)
/// <summary>
/// Gets the StyleCop settings.
/// </summary>
/// <param name="options">The analyzer options that will be used to determine the StyleCop settings.</param>
/// <param name="failureBehavior">The behavior of the method when a <see cref="JsonException"/> occurs while
/// deserializing the settings file.</param>
/// <param name="cancellationToken">The cancellation token that the operation will observe.</param>
/// <returns>A <see cref="StyleCopSettings"/> instance that represents the StyleCop settings for the given context.</returns>
internal static StyleCopSettings GetStyleCopSettings(this AnalyzerOptions options, DeserializationFailureBehavior failureBehavior, CancellationToken cancellationToken)
{
return GetStyleCopSettings(options != null ? options.AdditionalFiles : ImmutableArray.Create<AdditionalText>(), failureBehavior, cancellationToken);
}

private static StyleCopSettings GetStyleCopSettings(ImmutableArray<AdditionalText> additionalFiles, DeserializationFailureBehavior failureBehavior, CancellationToken cancellationToken)
{
try
{
Expand All @@ -74,7 +95,7 @@ private static StyleCopSettings GetStyleCopSettings(ImmutableArray<AdditionalTex
}
}
}
catch (JsonException)
catch (JsonException) when (failureBehavior == DeserializationFailureBehavior.ReturnDefaultSettings)
{
// The settings file is invalid -> return the default settings.
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

namespace StyleCop.Analyzers.SpecialRules
{
using System;
using System.Collections.Immutable;
using System.Globalization;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Newtonsoft.Json;

/// <summary>
/// The <em>stylecop.json</em> settings file could not be loaded due to a deserialization failure.
/// </summary>
[NoCodeFix("No automatic code fix is possible for general JSON syntax errors.")]
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class SA0002InvalidSettingsFile : DiagnosticAnalyzer
{
/// <summary>
/// The ID for diagnostics produced by the <see cref="SA0002InvalidSettingsFile"/> analyzer.
/// </summary>
public const string DiagnosticId = "SA0002";
private const string HelpLink = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA0002.md";
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(SpecialResources.SA0002Title), SpecialResources.ResourceManager, typeof(SpecialResources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(SpecialResources.SA0002MessageFormat), SpecialResources.ResourceManager, typeof(SpecialResources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(SpecialResources.SA0002Description), SpecialResources.ResourceManager, typeof(SpecialResources));

private static readonly DiagnosticDescriptor Descriptor =
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.SpecialRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);

private static readonly Action<CompilationAnalysisContext> CompilationAction = HandleCompilation;

/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
ImmutableArray.Create(Descriptor);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.RegisterCompilationAction(CompilationAction);
}

private static void HandleCompilation(CompilationAnalysisContext context)
{
try
{
SettingsHelper.GetStyleCopSettings(context.Options, DeserializationFailureBehavior.ThrowException, context.CancellationToken);
}
catch (JsonException ex)
{
string details = ex.Message;
string completeDescription = string.Format(Description.ToString(CultureInfo.CurrentCulture), details);

var completeDescriptor = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.SpecialRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, completeDescription, HelpLink);
context.ReportDiagnostic(Diagnostic.Create(completeDescriptor, Location.None));
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,15 @@ Note that some situations are not affected by the bug:
<data name="SA0001Title" xml:space="preserve">
<value>XML comment analysis disabled</value>
</data>
<data name="SA0002Description" xml:space="preserve">
<value>Various errors in the stylecop.json file can prevent the file from being loaded by the analyzers. In this case, the default settings are used instead.

{0}</value>
</data>
<data name="SA0002MessageFormat" xml:space="preserve">
<value>The stylecop.json settings file could not be loaded</value>
</data>
<data name="SA0002Title" xml:space="preserve">
<value>Invalid settings file</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@
<Compile Include="ReadabilityRules\SA1133DoNotCombineAttributes.cs" />
<Compile Include="ReadabilityRules\SA1134AttributesMustNotShareLine.cs" />
<Compile Include="ReadabilityRules\SX1101DoNotPrefixLocalMembersWithThis.cs" />
<Compile Include="Settings\DeserializationFailureBehavior.cs" />
<Compile Include="Settings\ObjectModel\DocumentationSettings.cs" />
<Compile Include="Settings\ObjectModel\EndOfFileHandling.cs" />
<Compile Include="Settings\ObjectModel\FileNamingConvention.cs" />
Expand Down Expand Up @@ -324,6 +325,7 @@
<Compile Include="SpacingRules\TokenSpacingProperties.cs" />
<Compile Include="SpecialRules\SA0000Roslyn7446Workaround.cs" />
<Compile Include="SpecialRules\SA0001XmlCommentAnalysisDisabled.cs" />
<Compile Include="SpecialRules\SA0002InvalidSettingsFile.cs" />
<Compile Include="SpecialRules\SpecialResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
Expand Down
1 change: 1 addition & 0 deletions StyleCopAnalyzers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "documentation", "documentat
documentation\KnownChanges.md = documentation\KnownChanges.md
documentation\SA0000Roslyn7446Workaround.md = documentation\SA0000Roslyn7446Workaround.md
documentation\SA0001.md = documentation\SA0001.md
documentation\SA0002.md = documentation\SA0002.md
documentation\SA1000.md = documentation\SA1000.md
documentation\SA1001.md = documentation\SA1001.md
documentation\SA1002.md = documentation\SA1002.md
Expand Down
Loading

0 comments on commit bd2f647

Please sign in to comment.