diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1518UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1518UnitTests.cs
index 828070f66..539607f8a 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1518UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1518UnitTests.cs
@@ -29,6 +29,8 @@ public void Bar(int i)
}
}";
+ private const string WhiteSpace = "\t ";
+
///
/// Verifies that blank lines at the end of the file will produce a warning.
///
@@ -49,6 +51,76 @@ internal async Task TestWithBlankLinesAtEndOfFileAsync(OptionSetting? newlineAtE
await VerifyCSharpFixAsync(newlineAtEndOfFile, testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}
+ ///
+ /// Verifies file with white space only and no cr/lf at end of file will produce a warning when setting requires.
+ ///
+ /// The effective setting.
+ /// The expected text to appear at the end of the file.
+ /// A representing the asynchronous unit test.
+ [Theory]
+ [InlineData(null, null)]
+ [InlineData(OptionSetting.Allow, null)]
+ [InlineData(OptionSetting.Require, "\r\n")]
+ [InlineData(OptionSetting.Omit, null)]
+ internal async Task TestWithWhiteSpaceOnlyAsync(OptionSetting? newlineAtEndOfFile, string expectedText)
+ {
+ var testCode = WhiteSpace;
+ var fixedCode = expectedText;
+
+ if (expectedText == null)
+ {
+ await VerifyCSharpDiagnosticAsync(newlineAtEndOfFile, testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
+ else
+ {
+ var expected = Diagnostic(this.GetDescriptor(newlineAtEndOfFile)).WithLocation(1, 1);
+ await VerifyCSharpFixAsync(newlineAtEndOfFile, testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Verifies file with white space only and cr/lf at end of file will produce a warning when setting requires.
+ ///
+ /// The effective setting.
+ /// The expected text to appear at the end of the file.
+ /// A representing the asynchronous unit test.
+ [Theory]
+ [InlineData(null, null)]
+ [InlineData(OptionSetting.Allow, null)]
+ [InlineData(OptionSetting.Require, null)]
+ [InlineData(OptionSetting.Omit, "")]
+ internal async Task TestWithWhiteSpaceAndNewlineOnlyAsync(OptionSetting? newlineAtEndOfFile, string expectedText)
+ {
+ var testCode = WhiteSpace + "\r\n";
+ var fixedCode = expectedText;
+
+ if (expectedText == null)
+ {
+ await VerifyCSharpDiagnosticAsync(newlineAtEndOfFile, testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
+ else
+ {
+ var expected = Diagnostic(this.GetDescriptor(newlineAtEndOfFile)).WithLocation(1, 1);
+ await VerifyCSharpFixAsync(newlineAtEndOfFile, testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Verifies that empty files will not produce a warning.
+ ///
+ /// The effective setting.
+ /// A representing the asynchronous unit test.
+ [Theory]
+ [InlineData(null)]
+ [InlineData(OptionSetting.Allow)]
+ [InlineData(OptionSetting.Require)]
+ [InlineData(OptionSetting.Omit)]
+ internal async Task TestWithEmptyFileAsync(OptionSetting? newlineAtEndOfFile)
+ {
+ var testCode = string.Empty;
+ await VerifyCSharpDiagnosticAsync(newlineAtEndOfFile, testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
+
///
/// Verifies that linefeed only blank lines at the end of the file will produce a warning.
///
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs
index c917df7a5..d9a41dac4 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs
@@ -72,6 +72,14 @@ public static bool IsWhitespaceOnly(this SyntaxTree tree, CancellationToken canc
&& TriviaHelper.IndexOfFirstNonWhitespaceTrivia(firstToken.LeadingTrivia) == -1;
}
+ public static bool IsEmpty(this SyntaxTree tree, CancellationToken cancellationToken)
+ {
+ var root = tree.GetRoot(cancellationToken);
+ var firstToken = root.GetFirstToken(includeZeroWidth: true);
+
+ return firstToken.IsKind(SyntaxKind.EndOfFileToken) && firstToken.FullSpan.IsEmpty;
+ }
+
internal static bool ContainsUsingAlias(this SyntaxTree tree, ConcurrentDictionary cache)
{
if (tree == null)
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1518UseLineEndingsCorrectlyAtEndOfFile.cs b/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1518UseLineEndingsCorrectlyAtEndOfFile.cs
index 90e697efa..7b306c8de 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1518UseLineEndingsCorrectlyAtEndOfFile.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1518UseLineEndingsCorrectlyAtEndOfFile.cs
@@ -185,6 +185,12 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCop
break;
}
+ if (context.Tree.IsEmpty(context.CancellationToken))
+ {
+ // Empty files never contain line endings.
+ return;
+ }
+
context.ReportDiagnostic(Diagnostic.Create(descriptorToReport, Location.Create(context.Tree, reportedSpan)));
}
}