Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
olsh committed Mar 9, 2019
1 parent b49a0ce commit 168e97d
Show file tree
Hide file tree
Showing 50 changed files with 3,146 additions and 0 deletions.
63 changes: 63 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,9 @@ ASALocalRun/

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# SonarQube
.sonarqube/

# Cake
tools/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# resharper-structured-logging

An extension for ReSharper that highlights structured logging templates and contains some useful analyzers
11 changes: 11 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 1.0.{build}
image: Visual Studio 2017
install:
- dotnet tool install -g Cake.Tool --version 0.30.0
build_script:
- cmd: dotnet cake -Target=CI
test: off
cache:
- '%USERPROFILE%\.sonar\cache'
- '%USERPROFILE%\.nuget\packages -> **\*.csproj'
- 'tools -> build.cake'
127 changes: 127 additions & 0 deletions build.cake
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#tool nuget:?package=MSBuild.SonarQube.Runner.Tool
#addin nuget:?package=Cake.Sonar

var target = Argument("target", "Default");
var buildConfiguration = Argument("buildConfig", "Debug");
var waveVersion = Argument("wave", "[182.0]");
var extensionsVersion = Argument("Version", "2018.2.3");

var solutionName = "ReSharper.Structured.Logging";
var projectName = solutionName;

var solutionFile = string.Format("./src/{0}.sln", solutionName);
var solutionFolder = string.Format("./src/{0}/", solutionName);
var projectFile = string.Format("{0}{1}.csproj", solutionFolder, projectName);

Task("AppendBuildNumber")
.WithCriteria(BuildSystem.AppVeyor.IsRunningOnAppVeyor)
.Does(() =>
{
var buildNumber = BuildSystem.AppVeyor.Environment.Build.Number;
extensionsVersion = string.Format("{0}.{1}", extensionsVersion, buildNumber);
});

Task("UpdateBuildVersion")
.IsDependentOn("AppendBuildNumber")
.WithCriteria(BuildSystem.AppVeyor.IsRunningOnAppVeyor)
.Does(() =>
{
BuildSystem.AppVeyor.UpdateBuildVersion(extensionsVersion);
});

Task("NugetRestore")
.Does(() =>
{
NuGetRestore(solutionFile);
});

Task("Build")
.IsDependentOn("NugetRestore")
.Does(() =>
{
MSBuild(solutionFile, new MSBuildSettings {
Configuration = buildConfiguration
});
});


Task("NugetPack")
.IsDependentOn("AppendBuildNumber")
.IsDependentOn("Build")
.Does(() =>
{
var buildPath = string.Format("./src/{0}/bin/{1}", solutionName, buildConfiguration);

var files = new List<NuSpecContent>();
files.Add(new NuSpecContent {Source = string.Format("{0}/{1}.dll", buildPath, projectName), Target = "dotFiles"});

if (buildConfiguration == "Debug")
{
files.Add(new NuSpecContent {Source = string.Format("{0}/{1}.pdb", buildPath, projectName), Target = "dotFiles"});
}

var nuGetPackSettings = new NuGetPackSettings {
Id = projectName,
Version = extensionsVersion,
Title = "Structure Logging",
Authors = new[] { "Oleg Shevchenko" },
Owners = new[] { "Oleg Shevchenko" },
Description = "Provides support for Serilog",
ProjectUrl = new Uri("https://github.com/olsh/resharper-structured-logging"),
LicenseUrl = new Uri("https://github.com/olsh/resharper-structured-logging/raw/master/LICENSE"),
Tags = new [] { "resharper", "serilog", "nlog", "logging", "structurelogging" },
RequireLicenseAcceptance= false,
Symbols = false,
NoPackageAnalysis = true,
Files = files,
OutputDirectory = ".",
Dependencies = new [] { new NuSpecDependency() { Id = "Wave", Version = waveVersion } },
ReleaseNotes = new [] { "https://github.com/olsh/resharper-structured-logging/releases" }
};

NuGetPack(nuGetPackSettings);
});

Task("SonarBegin")
.Does(() => {
SonarBegin(new SonarBeginSettings {
Url = "https://sonarcloud.io",
Login = EnvironmentVariable("sonar:apikey"),
Key = "resharper-structured-logging",
Name = "ReSharper Structured Logging",
ArgumentCustomization = args => args
.Append($"/o:olsh-github"),
Version = "1.0.0.0"
});
});

Task("SonarEnd")
.Does(() => {
SonarEnd(new SonarEndSettings {
Login = EnvironmentVariable("sonar:apikey")
});
});

Task("Sonar")
.IsDependentOn("SonarBegin")
.IsDependentOn("Build")
.IsDependentOn("SonarEnd");

Task("CreateArtifact")
.IsDependentOn("UpdateBuildVersion")
.IsDependentOn("NugetPack")
.WithCriteria(BuildSystem.AppVeyor.IsRunningOnAppVeyor)
.Does(() =>
{
var artifactFile = string.Format("{0}.{1}.nupkg", projectName, extensionsVersion);
BuildSystem.AppVeyor.UploadArtifact(artifactFile);
});

Task("CI")
.IsDependentOn("Sonar")
.IsDependentOn("CreateArtifact");

Task("Default")
.IsDependentOn("NugetPack");

RunTarget(target);
38 changes: 38 additions & 0 deletions src/ReSharper.Structured.Logging.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.438
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReSharper.Structured.Logging", "ReSharper.Structured.Logging\ReSharper.Structured.Logging.csproj", "{0B29307F-4E38-4611-BA0D-408A4B4F4E15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReSharper.Structured.Logging.Tests", "..\test\src\ReSharper.Structured.Logging.Tests.csproj", "{D07C40A7-39BE-4725-889F-FF2F2B781442}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8F7509A6-86AF-48A9-BD71-464ED879D8FA}"
ProjectSection(SolutionItems) = preProject
..\appveyor.yml = ..\appveyor.yml
..\build.cake = ..\build.cake
..\README.md = ..\README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B29307F-4E38-4611-BA0D-408A4B4F4E15}.Release|Any CPU.Build.0 = Release|Any CPU
{D07C40A7-39BE-4725-889F-FF2F2B781442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D07C40A7-39BE-4725-889F-FF2F2B781442}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D07C40A7-39BE-4725-889F-FF2F2B781442}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D07C40A7-39BE-4725-889F-FF2F2B781442}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2861CBD1-55F1-4AF9-8F6D-F2C345E8BB03}
EndGlobalSection
EndGlobal
139 changes: 139 additions & 0 deletions src/ReSharper.Structured.Logging/Analyzer/TemplateFormatAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System.Linq;

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

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

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

public TemplateFormatAnalyzer(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.CompiledValue);

HighlightTemplate(consumer, stringLiteral, messageTemplate);
HighlightUnusedArguments(element, consumer, templateArgument, messageTemplate);

var argumentsCount = element.ArgumentList.Arguments.Count - templateArgument.IndexOf() - 1;
if (messageTemplate.NamedProperties != null)
{
foreach (var property in messageTemplate.NamedProperties)
{
argumentsCount--;

if (argumentsCount < 0)
{
consumer.AddHighlighting(
new TemplateFormatStringInexistingArgumentWarning(
stringLiteral.Expression.GetDocumentRange()
.GetTokenDocumentRange(property)));
}
}
}
else if (messageTemplate.PositionalProperties != null)
{
foreach (var property in messageTemplate.PositionalProperties)
{
if (!property.TryGetPositionalValue(out int position))
{
continue;
}

if (position >= argumentsCount)
{
consumer.AddHighlighting(
new TemplateFormatStringInexistingArgumentWarning(
stringLiteral.Expression.GetDocumentRange()
.GetTokenDocumentRange(property)));
}
}
}
}

private static void HighlightTemplate(
IHighlightingConsumer consumer,
IStringLiteralAlterer stringLiteral,
MessageTemplate messageTemplate)
{
var documentRange = stringLiteral.Expression.GetDocumentRange();
foreach (var token in messageTemplate.Tokens)
{
if (!(token is PropertyToken))
{
continue;
}

consumer.AddHighlighting(
new StringEscapeCharacterHighlighting(
documentRange.GetTokenDocumentRange(token),
HighlightingAttributeIds.FORMAT_STRING_ITEM));
}
}

private static void HighlightUnusedArguments(
IInvocationExpression element,
IHighlightingConsumer consumer,
ICSharpArgument templateArgument,
MessageTemplate messageTemplate)
{
var templateArgumentIndex = templateArgument.IndexOf();
foreach (var argument in element.ArgumentList.Arguments)
{
var argumentIndex = argument.IndexOf();
if (argumentIndex <= templateArgumentIndex)
{
continue;
}

var argumentPosition = argumentIndex - templateArgumentIndex;
if (messageTemplate.NamedProperties != null)
{
if (messageTemplate.NamedProperties.Length < argumentPosition)
{
consumer.AddHighlighting(new TemplateFormatStringArgumentIsNotUsedWarning(argument.Expression));
}
}
else if (messageTemplate.PositionalProperties != null)
{
if (!messageTemplate.PositionalProperties.Any(
p => p.TryGetPositionalValue(out int position) && position == argumentPosition - 1))
{
consumer.AddHighlighting(new TemplateFormatStringArgumentIsNotUsedWarning(argument.Expression));
}
}
}
}
}
}
Loading

0 comments on commit 168e97d

Please sign in to comment.