Skip to content

Commit

Permalink
Implement positional properties analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
olsh committed Dec 20, 2019
1 parent f9cfc5e commit 14c2a7f
Show file tree
Hide file tree
Showing 17 changed files with 187 additions and 13 deletions.
2 changes: 1 addition & 1 deletion rules/AnonymousObjectDestructuringProblem.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##### Anonymous objects must be destructured
#### Anonymous objects must be destructured

Noncompliant Code Examples:
```csharp
Expand Down
2 changes: 1 addition & 1 deletion rules/ComplexObjectDestructuringProblem.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##### Complex objects with default `ToString()` implementation probably need to be destructured
#### Complex objects with default `ToString()` implementation probably need to be destructured

Noncompliant Code Example:
```csharp
Expand Down
2 changes: 1 addition & 1 deletion rules/ContextualLoggerProblem.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##### Incorrect type is used for contextual logger
#### Incorrect type is used for contextual logger

Noncompliant Code Examples:
```csharp
Expand Down
2 changes: 1 addition & 1 deletion rules/ExceptionPassedAsTemplateArgumentProblem.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##### Exception passed as a template argument
#### Exception passed as a template argument

Noncompliant Code Example:
```csharp
Expand Down
12 changes: 12 additions & 0 deletions rules/PositionalPropertyUsedProblem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#### Prefer named properties instead of positional ones

Noncompliant Code Examples:
```csharp
Log.Error("Disk quota {0} MB exceeded by {1}", quota, user);
```


Compliant Solution:
```csharp
Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
```
2 changes: 1 addition & 1 deletion rules/TemplateDuplicatePropertyProblem.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##### Duplicate template property
#### Duplicate template property

Noncompliant Code Example:
```csharp
Expand Down
2 changes: 1 addition & 1 deletion rules/TemplateIsNotCompileTimeConstantProblem.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
##### Message template is not a compile time constant
#### Message template is not a compile time constant

Noncompliant Code Examples:
```csharp
Expand Down
1 change: 1 addition & 0 deletions src/ReSharper.Structured.Logging.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rules", "Rules", "{D93C6901
..\rules\ComplexObjectDestructuringProblem.md = ..\rules\ComplexObjectDestructuringProblem.md
..\rules\ContextualLoggerProblem.md = ..\rules\ContextualLoggerProblem.md
..\rules\ExceptionPassedAsTemplateArgumentProblem.md = ..\rules\ExceptionPassedAsTemplateArgumentProblem.md
..\rules\PositionalPropertyUsedProblem.md = ..\rules\PositionalPropertyUsedProblem.md
..\rules\TemplateDuplicatePropertyProblem.md = ..\rules\TemplateDuplicatePropertyProblem.md
..\rules\TemplateIsNotCompileTimeConstantProblem.md = ..\rules\TemplateIsNotCompileTimeConstantProblem.md
EndProjectSection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Linq;

using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Util;

using ReSharper.Structured.Logging.Extensions;
using ReSharper.Structured.Logging.Highlighting;
using ReSharper.Structured.Logging.Serilog.Parsing;

namespace ReSharper.Structured.Logging.Analyzer
{
[ElementProblemAnalyzer(typeof(IInvocationExpression))]
public class PositionalPropertiesUsageAnalyzer : ElementProblemAnalyzer<IInvocationExpression>
{
private readonly MessageTemplateParser _messageTemplateParser;

public PositionalPropertiesUsageAnalyzer(MessageTemplateParser messageTemplateParser)
{
_messageTemplateParser = messageTemplateParser;
}

protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument();
if (templateArgument == null)
{
return;
}

var stringLiteral = StringLiteralAltererUtil.TryCreateStringLiteralByExpression(templateArgument.Value);
if (stringLiteral == null)
{
return;
}

var messageTemplate = _messageTemplateParser.Parse(stringLiteral.Expression.GetUnquotedText());
if (messageTemplate.PositionalProperties == null)
{
return;
}

foreach (var property in messageTemplate.PositionalProperties)
{
consumer.AddHighlighting(new PositionalPropertyUsedWarning(stringLiteral, property, stringLiteral.GetTokenDocumentRange(property)));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.Util;

using ReSharper.Structured.Logging.Highlighting;
using ReSharper.Structured.Logging.Serilog.Parsing;

[assembly:
RegisterConfigurableSeverity(
PositionalPropertyUsedWarning.SeverityId,
null,
HighlightingGroupIds.CompilerWarnings,
PositionalPropertyUsedWarning.Message,
PositionalPropertyUsedWarning.Message,
Severity.WARNING)]

namespace ReSharper.Structured.Logging.Highlighting
{
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class PositionalPropertyUsedWarning : IHighlighting
{
public const string Message = "Prefer named properties instead of positional ones";

public const string SeverityId = "PositionalPropertyUsedProblem";

public PositionalPropertyUsedWarning(
IStringLiteralAlterer stringLiteral,
PropertyToken namedProperty,
DocumentRange documentRange)
{
StringLiteral = stringLiteral;
NamedProperty = namedProperty;
Range = documentRange;
}

public string ErrorStripeToolTip => ToolTip;

public PropertyToken NamedProperty { get; }

public DocumentRange Range { get; }

public IStringLiteralAlterer StringLiteral { get; }

public string ToolTip => Message;

public DocumentRange CalculateRange()
{
return Range;
}

public bool IsValid()
{
return Range.IsValid();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public class StructuredLoggingWikiDataProvider : ICodeInspectionWikiDataProvider
{
ComplexObjectDestructuringWarning.SeverityId,
CreateSeverityUrl(ComplexObjectDestructuringWarning.SeverityId)
},
{
PositionalPropertyUsedWarning.SeverityId,
CreateSeverityUrl(PositionalPropertyUsedWarning.SeverityId)
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Serilog;

namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{0}", 1);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Serilog;

namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("||{0}|(0)|(1)", 1);
}
}
}

---------------------------------------------------------
(0): ReSharper Format String Item:
(1): ReSharper Warning: Prefer named properties instead of positional ones
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ namespace ConsoleApp
{
public static void Main()
{
Log.Logger.Information("|{0}|(0) ||{1}|(1)|(2)", 1);
Log.Logger.Information("|{1}|(3)", |1|(4), 2);
Log.Logger.Information("||{0}|(0)|(1) |||{1}|(2)|(3)|(4)", 1);
Log.Logger.Information("||{1}|(5)|(6)", |1|(7), 2);
}
}
}

---------------------------------------------------------
(0): ReSharper Format String Item:
(1): ReSharper Format String Item:
(2): ReSharper Warning: Non-existing argument in message template
(1): ReSharper Warning: Prefer named properties instead of positional ones
(2):<overlapped> ReSharper Warning: Non-existing argument in message template
(3): ReSharper Format String Item:
(4): ReSharper Dead Code: Argument is not used in message template
(4): ReSharper Warning: Prefer named properties instead of positional ones
(5): ReSharper Format String Item:
(6): ReSharper Warning: Prefer named properties instead of positional ones
(7): ReSharper Dead Code: Argument is not used in message template
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ namespace ConsoleApp
{
public static void Main()
{
Log.Logger.Information("|{0}|(0)", 1);
Log.Logger.Information("||{0}|(0)|(1)", 1);
}
}
}

---------------------------------------------------------
(0): ReSharper Format String Item:
(1): ReSharper Warning: Prefer named properties instead of positional ones
3 changes: 2 additions & 1 deletion test/src/Analyzer/MessageTemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ protected override bool HighlightingPredicate(
|| highlighting is AnonymousObjectDestructuringWarning
|| highlighting is ContextualLoggerWarning
|| highlighting is ExceptionPassedAsTemplateArgumentWarning
|| highlighting is ComplexObjectDestructuringWarning;
|| highlighting is ComplexObjectDestructuringWarning
|| highlighting is PositionalPropertyUsedWarning;
}
}
}
11 changes: 11 additions & 0 deletions test/src/Analyzer/PositionalPropertiesUsageAnalyzerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using NUnit.Framework;

namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class PositionalPropertiesUsageAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "PositionalPropertiesUsage";

[Test] public void TestSerilogPositionProperty() => DoNamedTest2();
}
}

0 comments on commit 14c2a7f

Please sign in to comment.