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

Port roslyn-analyer style rules to experimental rules in roslyn. #50358

Merged
merged 40 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8aacc22
Port 'wrap embedded statement' to Roslyn
CyrusNajmabadi Jan 9, 2021
ff4a8ff
Add options
CyrusNajmabadi Jan 9, 2021
3e5867a
Switch
CyrusNajmabadi Jan 9, 2021
fe54d8e
Move
CyrusNajmabadi Jan 9, 2021
d59d427
Fix
CyrusNajmabadi Jan 9, 2021
4d916ae
Port 'consecutive brace placement' to Roslyn
CyrusNajmabadi Jan 9, 2021
5af2e88
Add UI
CyrusNajmabadi Jan 9, 2021
2a41e0b
Port C# side of 'multiple blank lines' to Roslyn
CyrusNajmabadi Jan 9, 2021
baf628a
Add VB tests
CyrusNajmabadi Jan 9, 2021
4f855da
Refine
CyrusNajmabadi Jan 9, 2021
6152063
Fix
CyrusNajmabadi Jan 9, 2021
35431ed
Fix
CyrusNajmabadi Jan 9, 2021
64f10e3
Merge impls
CyrusNajmabadi Jan 9, 2021
027622e
Revert
CyrusNajmabadi Jan 9, 2021
afddafb
Use EnforceOnBuildValues
CyrusNajmabadi Jan 10, 2021
896066d
Port consecutive statement analyzer
CyrusNajmabadi Jan 10, 2021
6f698eb
Add VBside
CyrusNajmabadi Jan 10, 2021
10f5ad2
Add UI
CyrusNajmabadi Jan 10, 2021
ff8fcd5
VBtoo
CyrusNajmabadi Jan 10, 2021
c6cb66a
Add test
CyrusNajmabadi Jan 10, 2021
7d46c47
Invert values
CyrusNajmabadi Jan 11, 2021
5cfcadd
Fixup tests
CyrusNajmabadi Jan 11, 2021
6f10224
Fixup tests
CyrusNajmabadi Jan 11, 2021
186145b
Simplify
CyrusNajmabadi Jan 12, 2021
5b50c71
Add loop statement
CyrusNajmabadi Jan 12, 2021
1369ba6
Merge remote-tracking branch 'upstream/master' into experimentalForma…
CyrusNajmabadi Jan 14, 2021
1003442
Add fixer to put : on the same line as his/base
CyrusNajmabadi Jan 14, 2021
00e3545
Add UI
CyrusNajmabadi Jan 14, 2021
2999f4f
Merge remote-tracking branch 'upstream/master' into experimentalForma…
CyrusNajmabadi Jan 18, 2021
11c0c4e
Fixup tests
CyrusNajmabadi Jan 18, 2021
697aacb
Merge branch 'master' into experimentalFormatting
CyrusNajmabadi Jan 28, 2021
c154f74
Formatting
CyrusNajmabadi Jan 31, 2021
e7970d4
Merge remote-tracking branch 'upstream/master' into experimentalForma…
CyrusNajmabadi Feb 1, 2021
e3f3192
Use syntax facts
CyrusNajmabadi Feb 1, 2021
77646aa
Merge remote-tracking branch 'upstream/master' into experimentalForma…
CyrusNajmabadi Feb 9, 2021
2a81547
Update IDEDiagnosticIDConfigurationTests.cs
CyrusNajmabadi Feb 10, 2021
b70814c
Merge remote-tracking branch 'upstream/master' into experimentalForma…
CyrusNajmabadi Feb 10, 2021
00e925d
Fix tests
CyrusNajmabadi Feb 10, 2021
a31df02
Merge branch 'experimentalFormatting' of https://github.com/CyrusNajm…
CyrusNajmabadi Feb 10, 2021
7ab8b99
Fix tests
CyrusNajmabadi Feb 10, 2021
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
5 changes: 5 additions & 0 deletions src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<Compile Include="$(MSBuildThisFileDirectory)AddBraces\CSharpAddBracesDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AddRequiredParentheses\CSharpAddRequiredPatternParenthesesDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ConvertTypeofToNameof\CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NewLines\ConsecutiveBracePlacement\ConsecutiveBracePlacementDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NewLines\ConsecutiveStatementPlacement\CSharpConsecutiveStatementPlacementDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NewLines\ConstructorInitializerPlacement\ConstructorInitializerPlacementDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NewLines\MultipleBlankLines\CSharpMultipleBlankLinesDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)MatchFolderAndNamespace\CSharpMatchFolderAndNamespaceDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveRedundantEquality\CSharpRemoveRedundantEqualityDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RemoveUnnecessaryDiscardDesignation\CSharpRemoveUnnecessaryDiscardDesignationDiagnosticAnalyzer.cs" />
Expand Down Expand Up @@ -105,6 +109,7 @@
<Compile Include="$(MSBuildThisFileDirectory)UseSimpleUsingStatement\UseSimpleUsingStatementDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseThrowExpression\CSharpUseThrowExpressionDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ValidateFormatString\CSharpValidateFormatStringDiagnosticAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NewLines\EmbeddedStatementPlacement\EmbeddedStatementPlacementDiagnosticAnalyzer.cs" />
</ItemGroup>
<ItemGroup Condition="'$(DefaultLanguageSourceExtension)' != '' AND '$(BuildingInsideVisualStudio)' != 'true'">
<ExpectedCompile Include="$(MSBuildThisFileDirectory)**\*$(DefaultLanguageSourceExtension)" />
Expand Down
9 changes: 9 additions & 0 deletions src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,13 @@
<data name="Remove_unnessary_discard" xml:space="preserve">
<value>Remove unnecessary discard</value>
</data>
<data name="Embedded_statements_must_be_on_their_own_line" xml:space="preserve">
<value>Embedded statements must be on their own line</value>
</data>
<data name="Consecutive_braces_must_not_have_a_blank_between_them" xml:space="preserve">
<value>Consecutive braces must not have blank line between them</value>
</data>
<data name="Blank_line_not_allowed_after_constructor_initializer_colon" xml:space="preserve">
<value>Blank line not allowed after constructor initializer colon</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CSharp.NewLines.ConsecutiveBracePlacement
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class ConsecutiveBracePlacementDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer
{
public ConsecutiveBracePlacementDiagnosticAnalyzer()
: base(IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId,
EnforceOnBuildValues.ConsecutiveBracePlacement,
CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces,
LanguageNames.CSharp,
new LocalizableResourceString(
nameof(CSharpAnalyzersResources.Consecutive_braces_must_not_have_a_blank_between_them), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)))
{
}

public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis;

protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSyntaxTreeAction(AnalyzeTree);

private void AnalyzeTree(SyntaxTreeAnalysisContext context)
{
var option = context.GetOption(CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces);
if (option.Value)
return;

using var _ = ArrayBuilder<SyntaxNode>.GetInstance(out var stack);
Recurse(context, option.Notification.Severity, stack);
}

private void Recurse(SyntaxTreeAnalysisContext context, ReportDiagnostic severity, ArrayBuilder<SyntaxNode> stack)
{
var tree = context.Tree;
var cancellationToken = context.CancellationToken;

var root = tree.GetRoot(cancellationToken);
var text = tree.GetText(cancellationToken);

stack.Add(root);
while (stack.Count > 0)
{
cancellationToken.ThrowIfCancellationRequested();

var current = stack.Last();
stack.RemoveLast();

// Don't bother analyzing nodes that have syntax errors in them.
if (current.ContainsDiagnostics && current.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error))
continue;

foreach (var child in current.ChildNodesAndTokens())
{
if (child.IsNode)
stack.Add(child.AsNode()!);
else if (child.IsToken)
ProcessToken(context, severity, text, child.AsToken());
}
}
}

private void ProcessToken(SyntaxTreeAnalysisContext context, ReportDiagnostic severity, SourceText text, SyntaxToken token)
{
if (!HasExcessBlankLinesAfter(text, token, out var secondBrace, out _))
return;

context.ReportDiagnostic(DiagnosticHelper.Create(
this.Descriptor,
secondBrace.GetLocation(),
severity,
additionalLocations: null,
properties: null));
}

public static bool HasExcessBlankLinesAfter(
SourceText text, SyntaxToken token,
out SyntaxToken secondBrace,
out SyntaxTrivia endOfLineTrivia)
{
secondBrace = default;
endOfLineTrivia = default;
if (!token.IsKind(SyntaxKind.CloseBraceToken))
return false;

var nextToken = token.GetNextToken();
if (!nextToken.IsKind(SyntaxKind.CloseBraceToken))
return false;

var firstBrace = token;
secondBrace = nextToken;

// two } tokens. They need to be on the same line, or if they are not on subsequent lines, then there needs
// to be more than whitespace between them.
var lines = text.Lines;
var firstBraceLine = lines.GetLineFromPosition(firstBrace.SpanStart).LineNumber;
var secondBraceLine = lines.GetLineFromPosition(secondBrace.SpanStart).LineNumber;

var lineCount = secondBraceLine - firstBraceLine;

// if they're both on the same line, or one line apart, then there's no problem.
if (lineCount <= 1)
return false;

// they're multiple lines apart. This i not ok if those lines are all whitespace.
for (var currentLine = firstBraceLine + 1; currentLine < secondBraceLine; currentLine++)
{
if (!IsAllWhitespace(lines[currentLine]))
return false;
}

endOfLineTrivia = secondBrace.LeadingTrivia.Last(t => t.IsKind(SyntaxKind.EndOfLineTrivia));
return endOfLineTrivia != default;
}

private static bool IsAllWhitespace(TextLine textLine)
{
var text = textLine.Text!;
for (var i = textLine.Start; i < textLine.End; i++)
{
if (!SyntaxFacts.IsWhitespace(text[i]))
return false;
}

return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis.CSharp.LanguageServices;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.NewLines.ConsecutiveStatementPlacement;

namespace Microsoft.CodeAnalysis.CSharp.NewLines.ConsecutiveStatementPlacement
{
/// <summary>
/// Analyzer that finds code of the form:
/// <code>
/// if (cond)
/// {
/// }
/// NextStatement();
/// </code>
///
/// And requires it to be of the form:
/// <code>
/// if (cond)
/// {
/// }
///
/// NextStatement();
/// </code>
///
/// Specifically, all blocks followed by another statement must have a blank line between them.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpConsecutiveStatementPlacementDiagnosticAnalyzer : AbstractConsecutiveStatementPlacementDiagnosticAnalyzer<StatementSyntax>
{
public CSharpConsecutiveStatementPlacementDiagnosticAnalyzer()
: base(CSharpSyntaxFacts.Instance)
{
}

protected override bool IsBlockLikeStatement(SyntaxNode node)
=> node is BlockSyntax or SwitchStatementSyntax;

protected override Location GetDiagnosticLocation(SyntaxNode block)
=> block.GetLastToken().GetLocation();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Microsoft.CodeAnalysis.CSharp.NewLines.ConstructorInitializerPlacement
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class ConstructorInitializerPlacementDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer
{
public ConstructorInitializerPlacementDiagnosticAnalyzer()
: base(IDEDiagnosticIds.ConstructorInitializerPlacementDiagnosticId,
EnforceOnBuildValues.ConsecutiveBracePlacement,
CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer,
LanguageNames.CSharp,
new LocalizableResourceString(
nameof(CSharpAnalyzersResources.Blank_line_not_allowed_after_constructor_initializer_colon), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)))
{
}

public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis;

protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSyntaxTreeAction(AnalyzeTree);

private void AnalyzeTree(SyntaxTreeAnalysisContext context)
{
var option = context.GetOption(CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer);
if (option.Value)
return;

Recurse(context, option.Notification.Severity, context.Tree.GetRoot(context.CancellationToken));
}

private void Recurse(SyntaxTreeAnalysisContext context, ReportDiagnostic severity, SyntaxNode node)
{
context.CancellationToken.ThrowIfCancellationRequested();

// Don't bother analyzing nodes that have syntax errors in them.
if (node.ContainsDiagnostics)
return;

if (node is ConstructorInitializerSyntax initializer)
ProcessConstructorInitializer(context, severity, initializer);

foreach (var child in node.ChildNodesAndTokens())
{
if (child.IsNode)
Recurse(context, severity, child.AsNode()!);
}
}

private void ProcessConstructorInitializer(
SyntaxTreeAnalysisContext context, ReportDiagnostic severity, ConstructorInitializerSyntax initializer)
{
var sourceText = context.Tree.GetText(context.CancellationToken);

var colonToken = initializer.ColonToken;
var thisOrBaseKeyword = initializer.ThisOrBaseKeyword;

var colonLine = sourceText.Lines.GetLineFromPosition(colonToken.SpanStart);
var thisBaseLine = sourceText.Lines.GetLineFromPosition(thisOrBaseKeyword.SpanStart);
if (colonLine == thisBaseLine)
return;

if (colonToken.TrailingTrivia.Count == 0)
return;

if (colonToken.TrailingTrivia.Last().Kind() != SyntaxKind.EndOfLineTrivia)
return;

if (colonToken.TrailingTrivia.Any(t => !t.IsWhitespaceOrEndOfLine()))
return;

if (thisOrBaseKeyword.LeadingTrivia.Any(t => !t.IsWhitespaceOrEndOfLine() && !t.IsSingleOrMultiLineComment()))
return;

context.ReportDiagnostic(DiagnosticHelper.Create(
this.Descriptor,
colonToken.GetLocation(),
severity,
additionalLocations: ImmutableArray.Create(initializer.GetLocation()),
properties: null));
}
}
}
Loading