Repository: olsh/resharper-structured-logging
Branch: master
Commit: 2c05392577cb
Files: 220
Total size: 225.9 KB
Directory structure:
gitextract_1ugkx01o/
├── .editorconfig
├── .gitattributes
├── .github/
│ └── dependabot.yml
├── .gitignore
├── .nuke/
│ ├── build.schema.json
│ └── parameters.json
├── Directory.Build.props
├── LICENSE
├── README.md
├── appveyor.yml
├── build/
│ ├── .editorconfig
│ ├── Build.cs
│ ├── Directory.Build.props
│ ├── Directory.Build.targets
│ ├── ReSharper.Structured.Logging.nuspec
│ ├── _build.csproj
│ └── _build.csproj.DotSettings
├── build.cmd
├── build.gradle
├── build.ps1
├── build.sh
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── rules/
│ ├── AnonymousObjectDestructuringProblem.md
│ ├── ComplexObjectDestructuringProblem.md
│ ├── ComplexObjectInContextDestructuringProblem.md
│ ├── ContextualLoggerProblem.md
│ ├── ExceptionPassedAsTemplateArgumentProblem.md
│ ├── InconsistentContextLogPropertyNaming.md
│ ├── InconsistentLogPropertyNaming.md
│ ├── LogMessageIsSentenceProblem.md
│ ├── PositionalPropertyUsedProblem.md
│ ├── TemplateDuplicatePropertyProblem.md
│ └── TemplateIsNotCompileTimeConstantProblem.md
├── settings.gradle
├── src/
│ ├── .idea/
│ │ └── .idea.ReSharper.Structured.Logging/
│ │ └── .idea/
│ │ ├── .gitignore
│ │ ├── .name
│ │ ├── encodings.xml
│ │ ├── indexLayout.xml
│ │ └── vcs.xml
│ ├── .run/
│ │ ├── Pack ReSharper.run.xml
│ │ ├── Pack Rider.run.xml
│ │ ├── Test ReSharper.run.xml
│ │ └── Test Rider.run.xml
│ ├── ReSharper.Structured.Logging/
│ │ ├── Analyzer/
│ │ │ ├── AnonymousTypeDestructureAnalyzer.cs
│ │ │ ├── CompileTimeConstantTemplateAnalyzer.cs
│ │ │ ├── ComplexObjectDestructureAnalyzer.cs
│ │ │ ├── ContextualLoggerConstructorAnalyzer.cs
│ │ │ ├── ContextualLoggerSerilogFactoryAnalyzer.cs
│ │ │ ├── CorrectExceptionPassingAnalyzer.cs
│ │ │ ├── DuplicatePropertiesTemplateAnalyzer.cs
│ │ │ ├── LogMessageIsSentenceAnalyzer.cs
│ │ │ ├── PositionalPropertiesUsageAnalyzer.cs
│ │ │ └── PropertiesNamingAnalyzer.cs
│ │ ├── Caching/
│ │ │ └── TemplateParameterNameAttributeProvider.cs
│ │ ├── Extensions/
│ │ │ └── PsiExtensions.cs
│ │ ├── Highlighting/
│ │ │ ├── AnonymousObjectWithoutDestructuringWarning.cs
│ │ │ ├── ComplexObjectDestructuringInContextWarning.cs
│ │ │ ├── ComplexObjectDestructuringWarning.cs
│ │ │ ├── ComplexObjectDestructuringWarningBase.cs
│ │ │ ├── ContextualLoggerWarning.cs
│ │ │ ├── DuplicateTemplatePropertyWarning.cs
│ │ │ ├── ExceptionPassedAsTemplateArgumentWarning.cs
│ │ │ ├── InconsistentContextLogPropertyNamingWarning.cs
│ │ │ ├── InconsistentLogPropertyNamingWarning.cs
│ │ │ ├── InconsistentLogPropertyNamingWarningBase.cs
│ │ │ ├── LogMessageIsSentenceWarning.cs
│ │ │ ├── PositionalPropertyUsedWarning.cs
│ │ │ ├── TemplateFormatStringNonExistingArgumentWarning.cs
│ │ │ └── TemplateIsNotCompileTimeConstantWarning.cs
│ │ ├── Models/
│ │ │ └── MessageTemplateTokenInformation.cs
│ │ ├── QuickFixes/
│ │ │ ├── AddDestructuringToMessageTemplatePropertyFix.cs
│ │ │ ├── RemoveTrailingPeriodFix.cs
│ │ │ ├── RenameContextLogPropertyFix.cs
│ │ │ ├── RenameLogPropertyFix.cs
│ │ │ └── TemplateIsNotCompileTimeConstantFix.cs
│ │ ├── ReSharper.Structured.Logging.Rider.csproj
│ │ ├── ReSharper.Structured.Logging.csproj
│ │ ├── Serilog/
│ │ │ ├── Core/
│ │ │ │ └── IMessageTemplateParser.cs
│ │ │ ├── Events/
│ │ │ │ └── MessageTemplate.cs
│ │ │ └── Parsing/
│ │ │ ├── Alignment.cs
│ │ │ ├── AlignmentDirection.cs
│ │ │ ├── Destructuring.cs
│ │ │ ├── MessageTemplateParser.cs
│ │ │ ├── MessageTemplateToken.cs
│ │ │ ├── PropertyToken.cs
│ │ │ └── TextToken.cs
│ │ ├── Settings/
│ │ │ ├── PropertyNamingType.cs
│ │ │ ├── StructuredLoggingGroup.cs
│ │ │ ├── StructuredLoggingOptionsPage.cs
│ │ │ ├── StructuredLoggingSettings.cs
│ │ │ └── StructuredLoggingSettingsAccessor.cs
│ │ ├── Utils/
│ │ │ └── PropertyNameProvider.cs
│ │ ├── Wiki/
│ │ │ └── StructuredLoggingWikiDataProvider.cs
│ │ ├── ZoneMarker.cs
│ │ └── app.config
│ ├── ReSharper.Structured.Logging.sln
│ └── rider/
│ └── main/
│ ├── kotlin/
│ │ └── com/
│ │ └── jetbrains/
│ │ └── rider/
│ │ └── settings/
│ │ ├── StructuredLoggingBundle.kt
│ │ └── StructuredLoggingPluginOptionsPage.kt
│ └── resources/
│ ├── META-INF/
│ │ └── plugin.xml
│ └── messages/
│ └── StructuredLoggingBundle.properties
└── test/
├── data/
│ ├── Analyzers/
│ │ ├── AnonymousTypeDestructure/
│ │ │ ├── SerilogWithComplexPropertyWithoutDestructure.cs
│ │ │ ├── SerilogWithComplexPropertyWithoutDestructure.cs.gold
│ │ │ ├── SerilogWithoutDestructure.cs
│ │ │ └── SerilogWithoutDestructure.cs.gold
│ │ ├── ComplexTypeDestructure/
│ │ │ ├── SerilogContextExplicitDestructure.cs
│ │ │ ├── SerilogContextExplicitDestructure.cs.gold
│ │ │ ├── SerilogContextNumericWithoutDestructure.cs
│ │ │ ├── SerilogContextNumericWithoutDestructure.cs.gold
│ │ │ ├── SerilogContextWithoutDestructure.cs
│ │ │ ├── SerilogContextWithoutDestructure.cs.gold
│ │ │ ├── SerilogCustomExceptionWithoutDestructure.cs
│ │ │ ├── SerilogCustomExceptionWithoutDestructure.cs.gold
│ │ │ ├── SerilogDictionaryWithoutDestructure.cs
│ │ │ ├── SerilogDictionaryWithoutDestructure.cs.gold
│ │ │ ├── SerilogEnumerableWithoutDestructure.cs
│ │ │ ├── SerilogEnumerableWithoutDestructure.cs.gold
│ │ │ ├── SerilogForceStringWithoutDestructure.cs
│ │ │ ├── SerilogForceStringWithoutDestructure.cs.gold
│ │ │ ├── SerilogNullableWithoutDestructure.cs
│ │ │ ├── SerilogNullableWithoutDestructure.cs.gold
│ │ │ ├── SerilogNumericWithoutDestructure.cs
│ │ │ ├── SerilogNumericWithoutDestructure.cs.gold
│ │ │ ├── SerilogParentWithOverriddenToString.cs
│ │ │ ├── SerilogParentWithOverriddenToString.cs.gold
│ │ │ ├── SerilogWithoutDestructure.cs
│ │ │ └── SerilogWithoutDestructure.cs.gold
│ │ ├── ContextualLoggerConstructor/
│ │ │ ├── MicrosoftCorrectContextType.cs
│ │ │ ├── MicrosoftCorrectContextType.cs.gold
│ │ │ ├── MicrosoftWrongContextType.cs
│ │ │ ├── MicrosoftWrongContextType.cs.gold
│ │ │ ├── MicrosoftWrongContextTypeMultipleNamespaces.cs
│ │ │ ├── MicrosoftWrongContextTypeMultipleNamespaces.cs.gold
│ │ │ ├── MicrosoftWrongContextTypeMultipleParameters.cs
│ │ │ └── MicrosoftWrongContextTypeMultipleParameters.cs.gold
│ │ ├── ContextualLoggerSerilogFactory/
│ │ │ ├── SerilogCorrectContextType.cs
│ │ │ ├── SerilogCorrectContextType.cs.gold
│ │ │ ├── SerilogWrongContextType.cs
│ │ │ └── SerilogWrongContextType.cs.gold
│ │ ├── CorrectExceptionPassing/
│ │ │ ├── SerilogCorrectExceptionPassing.cs
│ │ │ ├── SerilogCorrectExceptionPassing.cs.gold
│ │ │ ├── SerilogIncorrectExceptionPassing.cs
│ │ │ ├── SerilogIncorrectExceptionPassing.cs.gold
│ │ │ ├── SerilogIncorrectExceptionPassingDynamicTemplate.cs
│ │ │ ├── SerilogIncorrectExceptionPassingDynamicTemplate.cs.gold
│ │ │ ├── SerilogMultipleExceptionPassing.cs
│ │ │ └── SerilogMultipleExceptionPassing.cs.gold
│ │ ├── DuplicatePropertiesTemplate/
│ │ │ ├── SerilogDuplicateNamedProperty.cs
│ │ │ └── SerilogDuplicateNamedProperty.cs.gold
│ │ ├── LogMessageIsSentence/
│ │ │ ├── SerilogNotSentenceMessage.cs
│ │ │ ├── SerilogNotSentenceMessage.cs.gold
│ │ │ ├── SerilogSentenceMessage.cs
│ │ │ └── SerilogSentenceMessage.cs.gold
│ │ ├── PositionalPropertiesUsage/
│ │ │ ├── SerilogPositionProperty.cs
│ │ │ └── SerilogPositionProperty.cs.gold
│ │ ├── PropertiesNamingAnalyzer/
│ │ │ ├── SerilogContextInterpolatedStringProperty.cs
│ │ │ ├── SerilogContextInterpolatedStringProperty.cs.gold
│ │ │ ├── SerilogContextInvalidNamedProperty.cs
│ │ │ ├── SerilogContextInvalidNamedProperty.cs.gold
│ │ │ ├── SerilogIgnoredInvalidNamedProperty.cs
│ │ │ ├── SerilogIgnoredInvalidNamedProperty.cs.gold
│ │ │ ├── SerilogInvalidElasticNamedProperty.cs
│ │ │ ├── SerilogInvalidElasticNamedProperty.cs.gold
│ │ │ ├── SerilogInvalidNamedProperty.cs
│ │ │ ├── SerilogInvalidNamedProperty.cs.gold
│ │ │ ├── SerilogInvalidNamedPropertyWithDot.cs
│ │ │ ├── SerilogInvalidNamedPropertyWithDot.cs.gold
│ │ │ ├── SerilogInvalidNamedPropertyWithSpace.cs
│ │ │ ├── SerilogInvalidNamedPropertyWithSpace.cs.gold
│ │ │ ├── SerilogInvalidSyntax.cs
│ │ │ ├── SerilogInvalidSyntax.cs.gold
│ │ │ ├── SerilogValidDestructuredNamedProperty.cs
│ │ │ ├── SerilogValidDestructuredNamedProperty.cs.gold
│ │ │ ├── SerilogValidNamedProperty.cs
│ │ │ └── SerilogValidNamedProperty.cs.gold
│ │ └── PropertiesNamingAnalyzerDotNet6/
│ │ ├── ZLoggerInvalidNamedProperty.cs
│ │ └── ZLoggerInvalidNamedProperty.cs.gold
│ ├── QuickFixes/
│ │ ├── AddDestructuringFix/
│ │ │ ├── SerilogEscapedString.cs
│ │ │ ├── SerilogEscapedString.cs.gold
│ │ │ ├── SerilogNewAnonymousObject.cs
│ │ │ ├── SerilogNewAnonymousObject.cs.gold
│ │ │ ├── SerilogNewComplexObject.cs
│ │ │ └── SerilogNewComplexObject.cs.gold
│ │ ├── RemoveTrailingPeriodFix/
│ │ │ ├── SerilogTrailingPeriod.cs
│ │ │ └── SerilogTrailingPeriod.cs.gold
│ │ ├── RenameContextLogPropertyFix/
│ │ │ ├── SerilogContextProperty.cs
│ │ │ └── SerilogContextProperty.cs.gold
│ │ └── RenameLogPropertyFix/
│ │ ├── SerilogDestructuredProperty.cs
│ │ ├── SerilogDestructuredProperty.cs.gold
│ │ ├── SerilogProperty.cs
│ │ ├── SerilogProperty.cs.gold
│ │ ├── SerilogPropertyConcatenated.cs
│ │ └── SerilogPropertyConcatenated.cs.gold
│ └── nuget.config
└── src/
├── Analyzer/
│ ├── AnonymousTypeDestructureAnalyzerTests.cs
│ ├── ComplexObjectDestructureAnalyzerTests.cs
│ ├── ContextualLoggerConstructorAnalyzerTests.cs
│ ├── ContextualLoggerSerilogFactoryAnalyzerTests.cs
│ ├── CorrectExceptionPassingAnalyzerTests.cs
│ ├── DuplicatePropertiesTemplateAnalyzerTests.cs
│ ├── LogMessageIsSentenceAnalyzerTests.cs
│ ├── MessageTemplateTests.cs
│ ├── PositionalPropertiesUsageAnalyzerTests.cs
│ ├── PropertiesElasticNamingAnalyzerTests.cs
│ ├── PropertiesIgnoredRegexNamingAnalyzerTests.cs
│ ├── PropertiesNamingAnalyzerDotNet6Tests.cs
│ └── PropertiesNamingAnalyzerTests.cs
├── Constants/
│ └── NugetPackages.cs
├── QuickFixes/
│ ├── AddDestructuringToMessageTemplatePropertyFixTests.cs
│ ├── QuickFixTestBase.cs
│ ├── RemoveTrailingPeriodFixTests.cs
│ ├── RenameContextLogPropertyFixTests.cs
│ └── RenameLogPropertyFixTests.cs
├── ReSharper.Structured.Logging.Rider.Tests.csproj
├── ReSharper.Structured.Logging.Tests.csproj
├── TestEnvironment.cs
└── app.config
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
indent_size = 4
[{*.csproj,*.json,*.yml,*.xml,*.props,*.nuspec,*.config}]
indent_size = 2
================================================
FILE: .gitattributes
================================================
###############################################################################
# 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
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "nuget"
directory: "/build/"
schedule:
interval: "monthly"
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# Rider Plugins
*.zip
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# SonarQube
.sonarqube/
# Cake
tools/
# Temp build folder
temp/
# Nuke temp files
.nuke/temp
# Gradle
.gradle/
gradle-build/
build/classes/
# Test data
/test/data/NuGetLocks/
================================================
FILE: .nuke/build.schema.json
================================================
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"Host": {
"type": "string",
"enum": [
"AppVeyor",
"AzurePipelines",
"Bamboo",
"Bitbucket",
"Bitrise",
"GitHubActions",
"GitLab",
"Jenkins",
"Rider",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI",
"VisualStudio",
"VSCode"
]
},
"ExecutableTarget": {
"type": "string",
"enum": [
"Clean",
"Compile",
"Pack",
"PackRiderPlugin",
"Sonar",
"SonarBegin",
"Test",
"UpdateBuildVersion",
"UploadReSharperArtifact",
"UploadRiderArtifact"
]
},
"Verbosity": {
"type": "string",
"description": "",
"enum": [
"Verbose",
"Normal",
"Minimal",
"Quiet"
]
},
"NukeBuild": {
"properties": {
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
"description": "Host for execution. Default is 'automatic'",
"$ref": "#/definitions/Host"
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
"Partition": {
"type": "string",
"description": "Partition to use on CI"
},
"Plan": {
"type": "boolean",
"description": "Shows the execution plan (HTML)"
},
"Profile": {
"type": "array",
"description": "Defines the profiles to load",
"items": {
"type": "string"
}
},
"Root": {
"type": "string",
"description": "Root directory during build execution"
},
"Skip": {
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
"$ref": "#/definitions/ExecutableTarget"
}
},
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
"$ref": "#/definitions/ExecutableTarget"
}
},
"Verbosity": {
"description": "Logging verbosity during build execution. Default is 'Normal'",
"$ref": "#/definitions/Verbosity"
}
}
}
},
"allOf": [
{
"properties": {
"Configuration": {
"type": "string"
},
"IsRiderHost": {
"type": "boolean"
},
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
}
}
},
{
"$ref": "#/definitions/NukeBuild"
}
]
}
================================================
FILE: .nuke/parameters.json
================================================
{
"$schema": "./build.schema.json",
"Solution": "src/ReSharper.Structured.Logging.sln"
}
================================================
FILE: Directory.Build.props
================================================
2025.1.0
$(MSBuildWarningsAsMessages);MSB3277;MSB3270
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 Oleg Shevchenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ReSharper Structured Logging
[](https://ci.appveyor.com/project/olsh/resharper-structured-logging)
[](https://sonarcloud.io/dashboard?id=resharper-structured-logging)
An extension for ReSharper and Rider IDE that highlights structured logging templates and contains some useful analyzers
> [The highlighting is a built-in feature starting from R#/Rider 2021.2](https://github.com/olsh/resharper-structured-logging/issues/35#issuecomment-900883583),
> but the extension still contains some useful analyzers that are not (yet) implemented by JetBrains team
At the moment it supports Serilog, NLog, Microsoft.Extensions.Logging and ZLogger
## Installation ReSharper
Look for `Structured Logging` in ReSharper -> Extension manager.
[JetBrains Plugins Repository](https://plugins.jetbrains.com/plugin/12083-structured-logging)
## Installation Rider
Look for `Structured Logging` in Settings -> Plugins -> Browse repositories.
[JetBrains Plugins Repository](https://plugins.jetbrains.com/plugin/12832-structured-logging)
## Highlighting

## Analyzers
* [Anonymous object is not destructured](rules/AnonymousObjectDestructuringProblem.md)
* [Complex object is not destructured](rules/ComplexObjectDestructuringProblem.md)
* [Complex object is not destructured in context](rules/ComplexObjectInContextDestructuringProblem.md)
* [Contextual logger mismatch](rules/ContextualLoggerProblem.md)
* [Exception passed as a template argument](rules/ExceptionPassedAsTemplateArgumentProblem.md)
* [Duplicate properties in a template](rules/TemplateDuplicatePropertyProblem.md)
* [Template should be a compile-time constant](rules/TemplateIsNotCompileTimeConstantProblem.md)
* [Prefer named properties instead of positional ones](rules/PositionalPropertyUsedProblem.md)
* [Inconsistent log property naming](rules/InconsistentLogPropertyNaming.md)
* [Inconsistent log property naming in context](rules/InconsistentContextLogPropertyNaming.md)
* [Log event messages should be fragments, not sentences](rules/LogMessageIsSentenceProblem.md)
## Turning Off Analyzers
Individual analyzers can be disabled as needed either through code comments or by adding a line to a project's
`.editorconfig` file.
### Turning Off Via Comments
The analyzer name can be used as-is in a ReSharper comment to disable an analyzer on a per-file or per-line basis.
For example:
```csharp
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
```
### Turning Off Via `.editorconfig`
To disable an analyzer for an entire directory, you can add a line to a `.editorconfig` file
([learn more](https://editorconfig.org)). In this case, the analyzer name needs to be converted to `snake_case`, prefixed
with `resharper_` and suffixed with `_highlighting`. For example:
```editorconfig
resharper_template_is_not_compile_time_constant_problem_highlighting = none
```
## Credits
Inspired by [SerilogAnalyzer](https://github.com/Suchiman/SerilogAnalyzer)
================================================
FILE: appveyor.yml
================================================
image: Visual Studio 2022
skip_branch_with_pr: true
skip_tags: true
install:
- SET JAVA_HOME=C:\Program Files\Java\jdk17
- SET PATH=%JAVA_HOME%\bin;%PATH%
build_script:
- cmd: >-
build.cmd update-build-version
build.cmd upload-resharper-artifact --configuration Release
build.cmd upload-rider-artifact --configuration Release --is-rider-host
build.cmd sonar --configuration Release
test: false
cache:
- '%USERPROFILE%\.sonar\cache'
- '.gradle -> build.gradle'
================================================
FILE: build/.editorconfig
================================================
[*.cs]
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
dotnet_style_require_accessibility_modifiers = never:warning
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_accessors = true:warning
================================================
FILE: build/Build.cs
================================================
using System.Text.RegularExpressions;
using Nuke.Common;
using Nuke.Common.CI;
using Nuke.Common.CI.AppVeyor;
using Nuke.Common.IO;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.NuGet;
using Nuke.Common.Tools.NUnit;
using Nuke.Common.Tools.SonarScanner;
using static Nuke.Common.EnvironmentInfo;
using static Nuke.Common.Tools.NUnit.NUnitTasks;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
using static Nuke.Common.Tools.SonarScanner.SonarScannerTasks;
using static Nuke.Common.Tools.NuGet.NuGetTasks;
[ShutdownDotNetAfterServerBuild]
class Build : NukeBuild
{
public static int Main() => Execute(x => x.Pack);
protected override void OnBuildInitialized()
{
SdkVersion = Project.GetProperty("SdkVersion");
SdkVersion.NotNull("Unable to detect SDK version");
var versionMatch = Regex.Match(SdkVersion, @"(?[\d\.]+)(?-.*)?");
SdkVersionWithoutSuffix = versionMatch.Groups["version"].ToString();
SdkVersionSuffix = versionMatch.Groups["suffix"].ToString();
ExtensionVersion = AppVeyor == null ? SdkVersion : $"{versionMatch.Groups["version"]}.{AppVeyor.BuildNumber}{versionMatch.Groups["suffix"]}";
var sdkMatch = Regex.Match(SdkVersion, @"\d{2}(\d{2}).(\d).*");
WaveMajorVersion = int.Parse(sdkMatch.Groups[1]
.Value + sdkMatch.Groups[2]
.Value);
WaveVersionsRange = $"{WaveMajorVersion}.0";
base.OnBuildInitialized();
}
[CI] readonly AppVeyor AppVeyor;
[Parameter] readonly string Configuration = "Release";
[Parameter] readonly bool IsRiderHost;
[Solution] readonly Solution Solution;
[LocalPath("./gradlew.bat")] readonly Tool Gradle;
[NuGetPackage(
packageId: "dotnet-cleanup",
packageExecutable: "cleanup.dll")]
readonly Tool DotNetCleanup;
string NuGetPackageFileName => $"{Project.Name}.{ExtensionVersion}.nupkg";
string NuGetPackagePath => RootDirectory / NuGetPackageFileName;
string RiderPackagePath => RootDirectory / "rider-structured-logging.zip";
string SonarQubeApiKey => GetVariable("sonar:apikey");
Project Project => IsRiderHost
? Solution.GetProject("ReSharper.Structured.Logging.Rider")
: Solution.GetProject("ReSharper.Structured.Logging");
Project TestProject => Solution.GetProject($"{Project.Name}.Tests");
AbsolutePath OutputDirectory => Project.Directory / "bin" / Project.Name / Configuration;
AbsolutePath TestProjectOutputDirectory => TestProject.Directory / "bin" / TestProject.Name / Configuration;
string ExtensionVersion { get; set; }
string SdkVersion { get; set; }
string SdkVersionSuffix { get; set; }
string SdkVersionWithoutSuffix { get; set; }
string WaveVersionsRange { get; set; }
int WaveMajorVersion { get; set; }
Target UpdateBuildVersion => _ => _
.Requires(() => AppVeyor)
.Executes(() =>
{
AppVeyor.Instance.UpdateBuildVersion(ExtensionVersion);
});
Target Clean => _ => _
.Executes(() =>
{
DotNetCleanup($"{Solution.Path} -y -v");
});
Target Compile => _ => _
.DependsOn()
.Executes(() =>
{
DotNetBuild(s => s
.SetProjectFile(Project.Path)
.SetConfiguration(Configuration)
.SetVersionPrefix(ExtensionVersion)
.SetOutputDirectory(OutputDirectory));
});
Target Test => _ => _
.Executes(() =>
{
DotNetBuild(s => s
.SetProjectFile(TestProject.Path)
.SetConfiguration(Configuration)
.SetOutputDirectory(TestProjectOutputDirectory));
NUnit3(s => s.SetInputFiles(TestProjectOutputDirectory / $"{TestProject.Name}.dll"));
});
Target Pack => _ => _
.DependsOn(Compile)
.Requires(() => !IsRiderHost)
.Executes(() =>
{
NuGetPack(s => s
.SetTargetPath(BuildProjectDirectory / "ReSharper.Structured.Logging.nuspec")
.SetVersion(ExtensionVersion)
.SetBasePath(OutputDirectory)
.AddProperty("project", Project.Name)
.AddProperty("waveVersion", WaveVersionsRange)
.SetOutputDirectory(RootDirectory));
});
Target PackRiderPlugin => _ => _
.DependsOn(Compile)
.Requires(() => IsRiderHost)
.Executes(() =>
{
// JetBrains is not very consistent in versioning
// https://github.com/olsh/resharper-structured-logging/issues/35#issuecomment-892764206
var productVersion = SdkVersionWithoutSuffix.TrimEnd('.', '0');
if (!string.IsNullOrEmpty(SdkVersionSuffix))
{
productVersion += $"{SdkVersionSuffix.Replace("0", string.Empty).ToUpper()}-SNAPSHOT";
}
Gradle($"buildPlugin -PPluginVersion={ExtensionVersion} -PProductVersion={productVersion} -PDotNetOutputDirectory={OutputDirectory} -PDotNetProjectName={Project.Name}", logger:
(_, s) =>
{
// Gradle writes warnings to stderr
// By default logger will write stderr as errors
// AppVeyor writes errors as special messages and stops the build if such messages more than 500
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
Serilog.Log.Debug(s);
});
(RootDirectory / "gradle-build" / "distributions" / $"rider-structured-logging-{ExtensionVersion}.zip").Copy(RiderPackagePath, ExistsPolicy.FileOverwrite);
});
Target SonarBegin => _ => _
.Unlisted()
.Before(Compile)
.Executes(() =>
{
SonarScannerBegin(s =>
{
s = s
.SetServer("https://sonarcloud.io")
.SetFramework("net5.0")
.SetLogin(SonarQubeApiKey)
.SetProjectKey("resharper-structured-logging")
.SetName("ReSharper Structured Logging")
.SetOrganization("olsh")
.SetVersion("1.0.0.0");
if (AppVeyor != null)
{
if (AppVeyor.PullRequestNumber != null)
{
s = s
.SetPullRequestKey(AppVeyor.PullRequestNumber.ToString())
.SetPullRequestBase(AppVeyor.RepositoryBranch)
.SetPullRequestBranch(AppVeyor.PullRequestHeadRepositoryBranch);
}
else
{
s = s
.SetBranchName(AppVeyor.RepositoryBranch);
}
}
return s;
});
});
Target Sonar => _ => _
.DependsOn(SonarBegin, Compile)
.Requires(() => !IsRiderHost)
.Executes(() =>
{
SonarScannerEnd(s => s
.SetLogin(SonarQubeApiKey)
.SetFramework("net5.0"));
});
Target UploadReSharperArtifact => _ => _
.DependsOn(Test, Pack)
.Requires(() => AppVeyor)
.Requires(() => !IsRiderHost)
.Executes(() =>
{
AppVeyor.PushArtifact(NuGetPackagePath);
});
Target UploadRiderArtifact => _ => _
.DependsOn(Test, PackRiderPlugin)
.Requires(() => AppVeyor)
.Requires(() => IsRiderHost)
.Executes(() =>
{
AppVeyor.PushArtifact(RiderPackagePath);
});
}
================================================
FILE: build/Directory.Build.props
================================================
================================================
FILE: build/Directory.Build.targets
================================================
================================================
FILE: build/ReSharper.Structured.Logging.nuspec
================================================
$project$
1.0.0
Structured Logging
Oleg Shevchenko
false
MIT
https://github.com/olsh/resharper-structured-logging
Contains some useful analyzers for structured logging. Supports Serilog, NLog, and Microsoft.Extensions.Logging.
https://github.com/olsh/resharper-structured-logging/releases
resharper serilog nlog templates logging structuredlogging
================================================
FILE: build/_build.csproj
================================================
Exe
net8.0
CS0649;CS0169
..
..
1
================================================
FILE: build/_build.csproj.DotSettings
================================================
DO_NOT_SHOW
DO_NOT_SHOW
DO_NOT_SHOW
DO_NOT_SHOW
Implicit
Implicit
ExpressionBody
0
NEXT_LINE
True
False
120
IF_OWNER_IS_SINGLE_LINE
WRAP_IF_LONG
False
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
<Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
True
True
True
True
True
True
True
True
True
True
================================================
FILE: build.cmd
================================================
:; set -eo pipefail
:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
:; ${SCRIPT_DIR}/build.sh "$@"
:; exit $?
@ECHO OFF
powershell -ExecutionPolicy ByPass -NoProfile "%~dp0build.ps1" %*
================================================
FILE: build.gradle
================================================
plugins {
id 'org.jetbrains.kotlin.jvm' version '2.3.10'
id 'org.jetbrains.intellij.platform' version '2.11.0'
}
buildDir = 'gradle-build'
version = ext.PluginVersion
compileKotlin {
kotlinOptions {
jvmTarget = "17"
}
}
sourceSets {
main {
java.srcDir 'src/rider/main/kotlin'
resources.srcDir 'src/rider/main/resources'
}
}
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
jetbrainsRuntime()
}
}
dependencies {
intellijPlatform {
rider(ProductVersion, false)
jetbrainsRuntime()
}
}
intellijPlatform {
pluginConfiguration {
name = rootProject.name
}
}
prepareSandbox {
def dotNetFiles = [
"${DotNetOutputDirectory}/${DotNetProjectName}.dll",
"${DotNetOutputDirectory}/${DotNetProjectName}.pdb",
]
dotNetFiles.forEach { f ->
def file = file(f)
from(file) {
into "${rootProject.name}/dotnet"
}
}
doLast {
dotNetFiles.forEach { f ->
if (!file(f).exists()) {
throw new RuntimeException("File $f does not exist")
}
}
}
}
wrapper {
gradleVersion = '8.13'
distributionType = Wrapper.DistributionType.ALL
distributionUrl = "https://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-${gradleVersion}-all.zip"
}
================================================
FILE: build.ps1
================================================
[CmdletBinding()]
Param(
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$BuildArguments
)
Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)"
Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
###########################################################################
# CONFIGURATION
###########################################################################
$BuildProjectFile = "$PSScriptRoot\build\_build.csproj"
$TempDirectory = "$PSScriptRoot\\.nuke\temp"
$DotNetGlobalFile = "$PSScriptRoot\\global.json"
$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
$DotNetChannel = "Current"
$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
$env:DOTNET_MULTILEVEL_LOOKUP = 0
###########################################################################
# EXECUTION
###########################################################################
function ExecSafe([scriptblock] $cmd) {
& $cmd
if ($LASTEXITCODE) { exit $LASTEXITCODE }
}
# If dotnet CLI is installed globally and it matches requested version, use for execution
if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
$(dotnet --version) -and $LASTEXITCODE -eq 0) {
$env:DOTNET_EXE = (Get-Command "dotnet").Path
}
else {
# Download install script
$DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
# If global.json exists, load expected version
if (Test-Path $DotNetGlobalFile) {
$DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
$DotNetVersion = $DotNetGlobal.sdk.version
}
}
# Install by channel or version
$DotNetDirectory = "$TempDirectory\dotnet-win"
if (!(Test-Path variable:DotNetVersion)) {
ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
} else {
ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
}
$env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
}
Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
================================================
FILE: build.sh
================================================
#!/usr/bin/env bash
bash --version 2>&1 | head -n 1
set -eo pipefail
SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
###########################################################################
# CONFIGURATION
###########################################################################
BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj"
TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp"
DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"
DOTNET_CHANNEL="Current"
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
export DOTNET_MULTILEVEL_LOOKUP=0
###########################################################################
# EXECUTION
###########################################################################
function FirstJsonValue {
perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}"
}
# If dotnet CLI is installed globally and it matches requested version, use for execution
if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then
export DOTNET_EXE="$(command -v dotnet)"
else
# Download install script
DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
mkdir -p "$TEMP_DIRECTORY"
curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
chmod +x "$DOTNET_INSTALL_FILE"
# If global.json exists, load expected version
if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then
DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")")
if [[ "$DOTNET_VERSION" == "" ]]; then
unset DOTNET_VERSION
fi
fi
# Install by channel or version
DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
if [[ -z ${DOTNET_VERSION+x} ]]; then
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
else
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
fi
export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
fi
echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-8.13-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Any property can be overwritten from command-line via
# -P=
PluginVersion=_PLACEHOLDER_
ProductVersion=_PLACEHOLDER_
DotNetOutputDirectory=./src/ReSharper.Structured.Logging/bin/ReSharper.Structured.Logging.Rider/Debug
DotNetProjectName=ReSharper.Structured.Logging.Rider
# We need to disable Gradle daemon after build otherwise the build hangs on build server
# https://github.com/appveyor/ci/issues/1745
org.gradle.daemon=false
# We need it to avoid bundle Kotlin jars into plugin
kotlin.stdlib.default.dependency=false
================================================
FILE: gradlew
================================================
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: rules/AnonymousObjectDestructuringProblem.md
================================================
#### Anonymous objects must be destructured
Noncompliant Code Examples:
```csharp
Log.Error("Processed {Position}", new { x = 4, y = 2});
```
Compliant Solution:
```csharp
Log.Error("Processed {@Position}", new { x = 4, y = 2});
```
================================================
FILE: rules/ComplexObjectDestructuringProblem.md
================================================
#### Complex objects with default `ToString()` implementation probably need to be destructured
Noncompliant Code Example:
```csharp
class User
{
public int Age { get; set; }
}
...
Log.Information("The user is {MyUser}", new User());
```
Compliant Solution:
```csharp
class User
{
public int Age { get; set; }
}
...
Log.Information("The user is {@MyUser}", new User());
// or
Log.Information("The user is {$MyUser}", new User());
```
================================================
FILE: rules/ComplexObjectInContextDestructuringProblem.md
================================================
#### Complex objects with default `ToString()` implementation probably need to be destructured
Noncompliant Code Example:
```csharp
class User
{
public int Age { get; set; }
}
...
LogContext.PushProperty("User", new User());
```
Compliant Solution:
```csharp
class User
{
public int Age { get; set; }
}
...
LogContext.PushProperty("User", new User(), true);
// or
LogContext.PushProperty("User", new User(), false);
```
================================================
FILE: rules/ContextualLoggerProblem.md
================================================
#### Incorrect type is used for contextual logger
Noncompliant Code Examples:
```csharp
class A
{
private static readonly ILogger Logger = Logger.ForContext();
}
class B {}
```
```csharp
class A
{
ILogger _log;
public A(ILogger log)
{
_log = log;
}
}
class B { }
```
Compliant Solution:
```csharp
class A
{
private static readonly ILogger Logger = Logger.ForContext();
}
class B {}
```
```csharp
class A
{
ILogger _log;
public A(ILogger log)
{
_log = log;
}
}
class B {}
```
================================================
FILE: rules/ExceptionPassedAsTemplateArgumentProblem.md
================================================
#### Exception passed as a template argument
Noncompliant Code Example:
```csharp
catch (Exception exception)
{
Log.Error(ex, "Disk quota {Quota} MB exceeded {Exception}", quota, exception);
}
```
Compliant Solution:
```csharp
catch (Exception exception)
{
Log.Error(exception, "Disk quota {Quota} MB exceeded", quota);
}
```
================================================
FILE: rules/InconsistentContextLogPropertyNaming.md
================================================
#### Inconsistent log property naming in context (can be configured in the extension settings)
Noncompliant Code Examples:
```csharp
LogContext.PushProperty("property_name", 1);
```
Compliant Solution:
```csharp
LogContext.PushProperty("PropertyName", 1);
```
================================================
FILE: rules/InconsistentLogPropertyNaming.md
================================================
#### Inconsistent log property naming (can be configured in the extension settings)
Noncompliant Code Examples:
```csharp
Log.Error("Processed {property_name}", 1);
```
Compliant Solution:
```csharp
Log.Error("Processed {PropertyName}", 1);
```
================================================
FILE: rules/LogMessageIsSentenceProblem.md
================================================
#### Log event messages should be fragments, not sentences
[https://benfoster.io/blog/serilog-best-practices/#message-template-recommendations](https://benfoster.io/blog/serilog-best-practices/#message-template-recommendations)
Noncompliant Code Examples:
```csharp
Log.Error("Disk quota {Quota} MB exceeded by {User}.", quota, user);
```
Compliant Solution:
```csharp
Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
```
================================================
FILE: rules/PositionalPropertyUsedProblem.md
================================================
#### 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);
```
================================================
FILE: rules/TemplateDuplicatePropertyProblem.md
================================================
#### Duplicate template property
Noncompliant Code Example:
```csharp
Log.Error("Disk quota {Quota} MB exceeded by {Quota}", quota, user);
```
Compliant Solution:
```csharp
Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
```
================================================
FILE: rules/TemplateIsNotCompileTimeConstantProblem.md
================================================
#### Message template is not a compile time constant
Noncompliant Code Examples:
```csharp
Log.Error($"Disk quota {quota} MB exceeded by {user}");
```
```csharp
Log.Error(string.Format("Disk quota {0} MB exceeded by {1}", quota, user));
```
Compliant Solution:
```csharp
Log.Error("Disk quota {Quota} MB exceeded by {User}", quota, user);
```
================================================
FILE: settings.gradle
================================================
rootProject.name = 'rider-structured-logging'
================================================
FILE: src/.idea/.idea.ReSharper.Structured.Logging/.idea/.gitignore
================================================
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.ReSharper.Structured.Logging.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# GitHub Copilot persisted chat sessions
/copilot/chatSessions
================================================
FILE: src/.idea/.idea.ReSharper.Structured.Logging/.idea/.name
================================================
ReSharper.Structured.Logging
================================================
FILE: src/.idea/.idea.ReSharper.Structured.Logging/.idea/encodings.xml
================================================
================================================
FILE: src/.idea/.idea.ReSharper.Structured.Logging/.idea/indexLayout.xml
================================================
================================================
FILE: src/.idea/.idea.ReSharper.Structured.Logging/.idea/vcs.xml
================================================
================================================
FILE: src/.run/Pack ReSharper.run.xml
================================================
================================================
FILE: src/.run/Pack Rider.run.xml
================================================
================================================
FILE: src/.run/Test ReSharper.run.xml
================================================
================================================
FILE: src/.run/Test Rider.run.xml
================================================
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/AnonymousTypeDestructureAnalyzer.cs
================================================
using System;
using System.Linq;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using ReSharper.Structured.Logging.Caching;
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 AnonymousTypeDestructureAnalyzer : ElementProblemAnalyzer
{
private readonly MessageTemplateParser _messageTemplateParser;
private readonly Lazy _templateParameterNameAttributeProvider;
public AnonymousTypeDestructureAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
{
_messageTemplateParser = messageTemplateParser;
_templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
}
protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
if (templateArgument == null)
{
return;
}
var anonymousObjectsArguments = element.ArgumentList.Arguments
.Where(a => a.Value is IAnonymousObjectCreationExpression)
.ToArray();
if (anonymousObjectsArguments.Length == 0)
{
return;
}
var templateText = templateArgument.TryGetTemplateText();
if (templateText == null)
{
return;
}
var messageTemplate = _messageTemplateParser.Parse(templateText);
if (messageTemplate.NamedProperties == null)
{
return;
}
var templateArgumentIndex = templateArgument.IndexOf();
foreach (var argument in anonymousObjectsArguments)
{
var index = argument.IndexOf() - templateArgumentIndex - 1;
if (index < messageTemplate.NamedProperties.Length)
{
var namedProperty = messageTemplate.NamedProperties[index];
if (namedProperty.Destructuring != Destructuring.Default)
{
continue;
}
var tokenInformation = templateArgument.GetTokenInformation(namedProperty);
consumer.AddHighlighting(new AnonymousObjectDestructuringWarning(tokenInformation));
}
}
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/CompileTimeConstantTemplateAnalyzer.cs
================================================
using System;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using ReSharper.Structured.Logging.Caching;
using ReSharper.Structured.Logging.Extensions;
using ReSharper.Structured.Logging.Highlighting;
namespace ReSharper.Structured.Logging.Analyzer
{
[ElementProblemAnalyzer(typeof(IInvocationExpression))]
public class CompileTimeConstantTemplateAnalyzer : ElementProblemAnalyzer
{
private readonly Lazy _templateParameterNameAttributeProvider;
public CompileTimeConstantTemplateAnalyzer(CodeAnnotationsCache codeAnnotationsCache)
{
_templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
}
protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
if (templateArgument?.Value == null)
{
return;
}
if (templateArgument.Value.IsConstantValue())
{
return;
}
consumer.AddHighlighting(new TemplateIsNotCompileTimeConstantWarning(element, templateArgument));
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/ComplexObjectDestructureAnalyzer.cs
================================================
using System;
using System.Linq;
using JetBrains.Metadata.Reader.API;
using JetBrains.Metadata.Reader.Impl;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Util;
using ReSharper.Structured.Logging.Caching;
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 ComplexObjectDestructureAnalyzer : ElementProblemAnalyzer
{
private static readonly IClrTypeName GenericDictionaryFqn = new ClrTypeName("System.Collections.Generic.Dictionary`2");
private readonly MessageTemplateParser _messageTemplateParser;
private readonly Lazy _templateParameterNameAttributeProvider;
public ComplexObjectDestructureAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
{
_messageTemplateParser = messageTemplateParser;
_templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
}
protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
CheckComplexObjectInTemplate(element, consumer);
CheckComplexObjectInContext(element, consumer);
}
private void CheckComplexObjectInContext(IInvocationExpression element, IHighlightingConsumer consumer)
{
if (!element.IsSerilogContextPushPropertyMethod())
{
return;
}
// Skip the analysis if explicit destructuring specified > 2 parameters
if (element.ArgumentList.Arguments.Count != 2)
{
return;
}
var valueArgument = element.ArgumentList.Arguments[1];
if (!CheckIfDestructureNeeded(valueArgument))
{
return;
}
consumer.AddHighlighting(new ComplexObjectDestructuringInContextWarning(element));
}
private void CheckComplexObjectInTemplate(IInvocationExpression element, IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
if (templateArgument == null)
{
return;
}
var templateText = templateArgument.TryGetTemplateText();
if (templateText == null)
{
return;
}
var messageTemplate = _messageTemplateParser.Parse(templateText);
if (messageTemplate.NamedProperties == null)
{
return;
}
var templateArgumentIndex = templateArgument.IndexOf();
var complexObject = element.ArgumentList.Arguments
.Where(a => a.IndexOf() > templateArgumentIndex)
.Where(CheckIfDestructureNeeded)
.ToArray();
if (complexObject.Length == 0)
{
return;
}
foreach (var argument in complexObject)
{
var index = argument.IndexOf() - templateArgumentIndex - 1;
if (index < messageTemplate.NamedProperties.Length)
{
var namedProperty = messageTemplate.NamedProperties[index];
if (namedProperty.Destructuring != Destructuring.Default)
{
continue;
}
consumer.AddHighlighting(
new ComplexObjectDestructuringWarning(templateArgument.GetTokenInformation(namedProperty)));
}
}
}
// ReSharper disable once CognitiveComplexity
private static bool CheckIfDestructureNeeded(ICSharpArgument argument)
{
bool CheckIfBaseToStringUsed(IType type, bool initialCheck = true)
{
// If we go through entire hierarchy and if didn't find overriden `ToString()` then this is an issue.
// But if the analyzed class is `object` itself, then this is not an issue.
if (type.IsObject())
{
return !initialCheck;
}
if (type.IsPredefinedNumeric())
{
return false;
}
if (type.IsString())
{
return false;
}
if (type.IsGuid())
{
return false;
}
var classType = type.GetClassType();
if (classType == null)
{
return false;
}
if (classType.Methods.Any(m => m.IsOverridesObjectToString()))
{
return false;
}
return CheckIfBaseToStringUsed(classType.GetBaseClassType(), false);
}
// ReSharper disable once StyleCop.SA1305
var iType = argument.GetExpressionType().ToIType();
if (iType == null)
{
return false;
}
if (iType.IsNullable())
{
var nullable = iType.GetNullableUnderlyingType();
if (nullable == null)
{
return false;
}
return CheckIfBaseToStringUsed(nullable);
}
if (iType is IDeclaredType declaredType)
{
if (Equals(declaredType.GetClrName(), GenericDictionaryFqn))
{
var argumentType = declaredType.GetFirstGenericArgumentType();
return argumentType != null && CheckIfBaseToStringUsed(argumentType);
}
var genericType = CollectionTypeUtil.GetElementTypesForGenericEnumerable(declaredType, false);
if (genericType.Count == 1)
{
return CheckIfBaseToStringUsed(genericType.Single());
}
}
return CheckIfBaseToStringUsed(iType);
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/ContextualLoggerConstructorAnalyzer.cs
================================================
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;
namespace ReSharper.Structured.Logging.Analyzer
{
[ElementProblemAnalyzer(typeof(IConstructorDeclaration))]
public class ContextualLoggerConstructorAnalyzer : ElementProblemAnalyzer
{
// ReSharper disable once CognitiveComplexity
protected override void Run(IConstructorDeclaration element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
{
if (element.Params?.ParameterDeclarations == null)
{
return;
}
foreach (var declaration in element.Params.ParameterDeclarations)
{
if (!(declaration.Type is IDeclaredType declaredType))
{
continue;
}
if (!declaredType.IsGenericMicrosoftExtensionsLogger())
{
continue;
}
var argumentType = declaredType.GetFirstGenericArgumentType();
if (argumentType == null)
{
continue;
}
var containingType = element.DeclaredElement?.GetContainingType();
var className = containingType?.GetClrName().FullName;
if (className == null)
{
continue;
}
if (className.Equals(argumentType.GetClassType()?.GetClrName().FullName))
{
continue;
}
consumer.AddHighlighting(new ContextualLoggerWarning(declaration.TypeUsage.GetDocumentRange()));
}
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/ContextualLoggerSerilogFactoryAnalyzer.cs
================================================
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
using ReSharper.Structured.Logging.Extensions;
using ReSharper.Structured.Logging.Highlighting;
namespace ReSharper.Structured.Logging.Analyzer
{
[ElementProblemAnalyzer(typeof(IInvocationExpression))]
public class ContextualLoggerSerilogFactoryAnalyzer : ElementProblemAnalyzer
{
protected override void Run(IInvocationExpression element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
{
if (!element.IsSerilogContextFactoryLogger())
{
return;
}
var containingNode = element.GetContainingNode();
if (containingNode == null)
{
return;
}
var invocationTypeArgument = element.TypeArguments[0]?.GetScalarType()?.GetClrName();
if (invocationTypeArgument?.FullName == containingNode.CLRName)
{
return;
}
consumer.AddHighlighting(new ContextualLoggerWarning(element.GetDocumentRange()));
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/CorrectExceptionPassingAnalyzer.cs
================================================
using System;
using System.Linq;
using JetBrains.Metadata.Reader.API;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Util;
using ReSharper.Structured.Logging.Caching;
using ReSharper.Structured.Logging.Extensions;
using ReSharper.Structured.Logging.Highlighting;
namespace ReSharper.Structured.Logging.Analyzer
{
[ElementProblemAnalyzer(typeof(IInvocationExpression))]
public class CorrectExceptionPassingAnalyzer : ElementProblemAnalyzer
{
private readonly Lazy _templateParameterNameAttributeProvider;
public CorrectExceptionPassingAnalyzer(CodeAnnotationsCache codeAnnotationsCache)
{
_templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
}
// ReSharper disable once CognitiveComplexity
protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
if (templateArgument == null)
{
return;
}
var exceptionType = element.PsiModule.GetPredefinedType().TryGetType(PredefinedType.EXCEPTION_FQN, NullableAnnotation.Unknown);
if (exceptionType == null)
{
return;
}
ICSharpArgument invalidExceptionArgument = FindInvalidExceptionArgument(element, templateArgument, exceptionType);
if (invalidExceptionArgument == null)
{
return;
}
var overloadAvailable = false;
var candidates = element.InvocationExpressionReference.GetCandidates().ToArray();
var invalidArgumentIndex = invalidExceptionArgument.IndexOf();
foreach (var candidate in candidates)
{
if (!(candidate.GetDeclaredElement() is IMethod declaredElement))
{
continue;
}
foreach (var parameter in declaredElement.Parameters)
{
if (invalidArgumentIndex <= parameter.IndexOf())
{
break;
}
if (parameter.Type.IsSubtypeOf(exceptionType))
{
overloadAvailable = true;
break;
}
}
if (overloadAvailable)
{
break;
}
}
if (!overloadAvailable)
{
return;
}
consumer.AddHighlighting(new ExceptionPassedAsTemplateArgumentWarning(invalidExceptionArgument.GetDocumentRange()));
}
private ICSharpArgument FindInvalidExceptionArgument(IInvocationExpression invocationExpression, ICSharpArgument templateArgument, IDeclaredType exceptionType)
{
var templateArgumentIndex = templateArgument.IndexOf();
foreach (var argument in invocationExpression.ArgumentList.Arguments)
{
var argumentType = argument.Value?.Type();
if (!(argumentType is IDeclaredType declaredType))
{
continue;
}
if (!declaredType.IsSubtypeOf(exceptionType))
{
continue;
}
if (templateArgumentIndex > argument.IndexOf())
{
return null;
}
return argument;
}
return null;
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/DuplicatePropertiesTemplateAnalyzer.cs
================================================
using System;
using System.Linq;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using ReSharper.Structured.Logging.Caching;
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 DuplicatePropertiesTemplateAnalyzer : ElementProblemAnalyzer
{
private readonly MessageTemplateParser _messageTemplateParser;
private readonly Lazy _templateParameterNameAttributeProvider;
public DuplicatePropertiesTemplateAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
{
_messageTemplateParser = messageTemplateParser;
_templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
}
protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
var templateText = templateArgument?.TryGetTemplateText();
if (templateText == null)
{
return;
}
var messageTemplate = _messageTemplateParser.Parse(templateText);
if (messageTemplate.NamedProperties == null)
{
return;
}
foreach (var duplicates in messageTemplate.NamedProperties
.GroupBy(n => n.PropertyName)
.Where(g => g.Count() > 1))
{
foreach (var token in duplicates)
{
consumer.AddHighlighting(new DuplicateTemplatePropertyWarning(templateArgument.GetTokenInformation(token)));
}
}
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/LogMessageIsSentenceAnalyzer.cs
================================================
using System;
using System.Text.RegularExpressions;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Util;
using ReSharper.Structured.Logging.Caching;
using ReSharper.Structured.Logging.Extensions;
using ReSharper.Structured.Logging.Highlighting;
namespace ReSharper.Structured.Logging.Analyzer
{
[ElementProblemAnalyzer(typeof(IInvocationExpression), HighlightingTypes = new[] { typeof(LogMessageIsSentenceWarning) })]
public class LogMessageIsSentenceAnalyzer : ElementProblemAnalyzer
{
private readonly Lazy _templateParameterNameAttributeProvider;
private static readonly Regex DotAtTheEnd = new Regex(@"(?();
}
protected override void Run(IInvocationExpression element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
var lastFragmentExpression = templateArgument?.TryCreateLastTemplateFragmentExpression();
if (lastFragmentExpression == null)
{
return;
}
var unquotedText = lastFragmentExpression.Expression.GetUnquotedText();
if (!DotAtTheEnd.IsMatch(unquotedText))
{
return;
}
consumer.AddHighlighting(new LogMessageIsSentenceWarning(lastFragmentExpression, DotAtTheEnd));
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/PositionalPropertiesUsageAnalyzer.cs
================================================
using System;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using ReSharper.Structured.Logging.Caching;
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
{
private readonly MessageTemplateParser _messageTemplateParser;
private readonly Lazy _templateParameterNameAttributeProvider;
public PositionalPropertiesUsageAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
{
_messageTemplateParser = messageTemplateParser;
_templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
}
protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
var templateText = templateArgument?.TryGetTemplateText();
if (templateText == null)
{
return;
}
var messageTemplate = _messageTemplateParser.Parse(templateText);
if (messageTemplate.PositionalProperties == null)
{
return;
}
foreach (var property in messageTemplate.PositionalProperties)
{
consumer.AddHighlighting(new PositionalPropertyUsedWarning(templateArgument.GetTokenInformation(property)));
}
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Analyzer/PropertiesNamingAnalyzer.cs
================================================
using System;
using System.Text.RegularExpressions;
using JetBrains.Application.Settings;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CodeAnnotations;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
using ReSharper.Structured.Logging.Caching;
using ReSharper.Structured.Logging.Extensions;
using ReSharper.Structured.Logging.Highlighting;
using ReSharper.Structured.Logging.Serilog.Parsing;
using ReSharper.Structured.Logging.Services;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Analyzer;
[ElementProblemAnalyzer(typeof(IInvocationExpression))]
public class PropertiesNamingAnalyzer : ElementProblemAnalyzer
{
private readonly MessageTemplateParser _messageTemplateParser;
private readonly Lazy _templateParameterNameAttributeProvider;
public PropertiesNamingAnalyzer(MessageTemplateParser messageTemplateParser, CodeAnnotationsCache codeAnnotationsCache)
{
_messageTemplateParser = messageTemplateParser;
_templateParameterNameAttributeProvider = codeAnnotationsCache.GetLazyProvider();
}
protected override void Run(
IInvocationExpression element,
ElementProblemAnalyzerData data,
IHighlightingConsumer consumer)
{
var settingsStore = element.GetProject()
?.GetSolution()
.GetSettingsStore();
var ignoreRegexString = settingsStore?.GetValue(StructuredLoggingSettingsAccessor.IgnoredPropertiesRegex);
var ignoredPropertiesRegex = string.IsNullOrWhiteSpace(ignoreRegexString) ? null : new Regex(ignoreRegexString);
CheckPropertiesInTemplate(element, consumer, settingsStore, ignoredPropertiesRegex);
CheckPropertiesInContext(element, consumer, settingsStore, ignoredPropertiesRegex);
}
private void CheckPropertiesInTemplate(
IInvocationExpression element,
IHighlightingConsumer consumer,
IContextBoundSettingsStore settingsStore,
Regex ignoredPropertiesRegex)
{
var templateArgument = element.GetTemplateArgument(_templateParameterNameAttributeProvider.Value);
var templateText = templateArgument?.TryGetTemplateText();
if (templateText == null)
{
return;
}
var messageTemplate = _messageTemplateParser.Parse(templateText);
if (messageTemplate.NamedProperties == null)
{
return;
}
foreach (var property in messageTemplate.NamedProperties)
{
if (string.IsNullOrEmpty(property.PropertyName))
{
continue;
}
var suggestedName = GetSuggestedName(property.PropertyName, settingsStore, ignoredPropertiesRegex);
if (string.Equals(suggestedName, property.PropertyName))
{
continue;
}
consumer.AddHighlighting(
new InconsistentLogPropertyNamingWarning(templateArgument.GetTokenInformation(property), property,
suggestedName));
}
}
private void CheckPropertiesInContext(
IInvocationExpression element,
IHighlightingConsumer consumer,
IContextBoundSettingsStore settingsStore,
Regex ignoredPropertiesRegex)
{
if (!element.IsSerilogContextPushPropertyMethod())
{
return;
}
if (element.ArgumentList.Arguments.Count < 1)
{
return;
}
var propertyArgument = element.ArgumentList.Arguments[0];
var propertyName = string.Empty;
propertyArgument.Value?.ConstantValue.IsString(out propertyName);
if (string.IsNullOrEmpty(propertyName))
{
return;
}
var suggestedName = GetSuggestedName(propertyName, settingsStore, ignoredPropertiesRegex);
if (string.Equals(propertyName, suggestedName))
{
return;
}
consumer.AddHighlighting(new InconsistentContextLogPropertyNamingWarning(propertyArgument, propertyName, suggestedName));
}
private string GetSuggestedName(string propertyName, IContextBoundSettingsStore settingsStore, Regex ignoredPropertiesRegex)
{
if (ignoredPropertiesRegex != null && ignoredPropertiesRegex.IsMatch(propertyName))
{
return propertyName;
}
return PropertyNameProvider.GetSuggestedName(propertyName, settingsStore);
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Caching/TemplateParameterNameAttributeProvider.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Application.Parts;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CodeAnnotations;
namespace ReSharper.Structured.Logging.Caching;
[CodeAnnotationProvider(Instantiation.DemandAnyThreadUnsafe)]
public class TemplateParameterNameAttributeProvider(
AttributeInstancesProvider attributeInstancesProvider,
CodeAnnotationsConfiguration codeAnnotationsConfiguration)
: CodeAnnotationInfoProvider(attributeInstancesProvider, codeAnnotationsConfiguration, true)
{
private const string MessageTemplateFormatMethodAttribute = "MessageTemplateFormatMethodAttribute";
protected override string CalculateInfo(ITypeMember attributesOwner, IEnumerable attributeInstances)
{
var templateFormatAttribute = attributeInstances
.FirstOrDefault(a => string.Equals(a.GetAttributeShortName(), MessageTemplateFormatMethodAttribute, StringComparison.Ordinal));
if (templateFormatAttribute != null)
{
return templateFormatAttribute.PositionParameters()
.FirstOrDefault()
?.ConstantValue.StringValue;
}
var className = attributesOwner.ContainingType?.GetClrName().FullName;
if (className == "Microsoft.Extensions.Logging.LoggerExtensions")
{
return attributesOwner.ShortName == "BeginScope" ? "messageFormat" : "message";
}
if (className == "ZLogger.ZLoggerExtensions")
{
return "format";
}
return null;
}
protected override string GetDefaultInfo(ITypeMember attributesOwner)
{
return null;
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Extensions/PsiExtensions.cs
================================================
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using JetBrains.DocumentModel;
using JetBrains.Metadata.Reader.API;
using JetBrains.Metadata.Reader.Impl;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp.Impl.Resolve;
using JetBrains.ReSharper.Psi.CSharp.Parsing;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Util;
using JetBrains.Util;
using ReSharper.Structured.Logging.Caching;
using ReSharper.Structured.Logging.Models;
using ReSharper.Structured.Logging.Serilog.Parsing;
namespace ReSharper.Structured.Logging.Extensions
{
public static class PsiExtensions
{
private static readonly IClrTypeName LogContextFqn = new ClrTypeName("Serilog.Context.LogContext");
[CanBeNull]
public static ICSharpArgument GetTemplateArgument(this IInvocationExpression invocationExpression, TemplateParameterNameAttributeProvider templateParameterNameAttributeProvider)
{
if (!(invocationExpression.Reference.Resolve().DeclaredElement is ITypeMember typeMember))
{
return null;
}
var templateParameterName = templateParameterNameAttributeProvider.GetInfo(typeMember);
if (string.IsNullOrEmpty(templateParameterName))
{
return null;
}
foreach (var argument in invocationExpression.ArgumentList.Arguments)
{
if (argument.MatchingParameter?.Element.ShortName == templateParameterName)
{
return argument;
}
}
return null;
}
public static MessageTemplateTokenInformation GetTokenInformation(this ICSharpArgument argument, MessageTemplateToken token)
{
var (tokenTextRange, tokenArgument) = FindTokenTextRange(argument, token);
var tokenDocument = argument.GetDocumentRange().Document;
var documentRange = new DocumentRange(tokenDocument, tokenTextRange);
return new MessageTemplateTokenInformation(documentRange, tokenArgument);
}
// ReSharper disable once CognitiveComplexity
private static (TextRange, IStringLiteralAlterer) FindTokenTextRange(this ICSharpArgument argument, MessageTemplateToken token)
{
if (argument.Value is IAdditiveExpression additiveExpression && additiveExpression.ConstantValue.IsString())
{
var arguments = new LinkedList();
FlattenAdditiveExpression(additiveExpression, arguments);
var globalOffset = 0;
foreach (var additiveArgument in arguments)
{
var range = additiveArgument.GetDocumentRange();
var start = range.StartOffset.Offset;
var end = range.EndOffset.Offset;
// Usually there are two quotes in the string expression
// But if it's a verbatim string, we should count @ symbol as well
var isVerbatimString = additiveArgument.Expression.IsVerbatimString();
var nonTemplateTokenCount = isVerbatimString ? 3 : 2;
// The token index is zero-based so we need to subtract 1
if (token.StartIndex < end - start - 1 - nonTemplateTokenCount + globalOffset)
{
var tokenStartIndex = start + token.StartIndex - globalOffset + 1;
if (isVerbatimString)
{
tokenStartIndex++;
}
var tokenEndIndex = tokenStartIndex + token.Length;
return (new TextRange(tokenStartIndex, end > tokenEndIndex ? tokenEndIndex : end), StringLiteralAltererUtil.TryCreateStringLiteralByExpression(additiveArgument.Expression));
}
globalOffset += end - start - nonTemplateTokenCount;
}
}
var startOffset = argument.GetDocumentRange().TextRange.StartOffset + token.StartIndex + 1;
if (argument.Expression.IsVerbatimString())
{
startOffset++;
}
// ReSharper disable once AssignNullToNotNullAttribute
return (new TextRange(startOffset, startOffset + token.Length), StringLiteralAltererUtil.TryCreateStringLiteralByExpression(argument.Expression));
}
public static string TryGetTemplateText(this ICSharpArgument argument)
{
if (argument.Value is IAdditiveExpression additiveExpression && additiveExpression.ConstantValue.IsString())
{
var linkedList = new LinkedList();
FlattenAdditiveExpression(additiveExpression, linkedList);
return string.Join(string.Empty, linkedList.Select(l => l.Expression.GetExpressionText()));
}
return argument.Value.GetExpressionText();
}
[CanBeNull]
public static IStringLiteralAlterer TryCreateLastTemplateFragmentExpression(this ICSharpArgument argument)
{
if (argument.Value is IAdditiveExpression additiveExpression && additiveExpression.ConstantValue.IsString())
{
var argumentInfo = additiveExpression.Arguments.Last();
if (argumentInfo is ExpressionArgumentInfo expressionArgumentInfo)
{
return StringLiteralAltererUtil.TryCreateStringLiteralByExpression(expressionArgumentInfo.Expression);
}
return null;
}
return argument.Value == null ? null : StringLiteralAltererUtil.TryCreateStringLiteralByExpression(argument.Value);
}
public static bool IsGenericMicrosoftExtensionsLogger([NotNull]this IDeclaredType declared)
{
return declared.GetClrName().FullName == "Microsoft.Extensions.Logging.ILogger`1";
}
public static bool IsSerilogContextFactoryLogger([NotNull]this IInvocationExpression invocationExpression)
{
if (invocationExpression.TypeArguments.Count != 1)
{
return false;
}
var declaredElement = invocationExpression.Reference.Resolve().DeclaredElement as IClrDeclaredElement;
var containingType = declaredElement?.GetContainingType();
if (containingType == null)
{
return false;
}
if (containingType.GetClrName().FullName == "Serilog.ILogger" && declaredElement.ShortName == "ForContext")
{
return true;
}
return false;
}
public static bool IsSerilogContextPushPropertyMethod(this IInvocationExpression invocationExpression)
{
var typeMember = invocationExpression.Reference.Resolve().DeclaredElement as ITypeMember;
var containingType = typeMember?.GetContainingType();
if (containingType == null)
{
return false;
}
return LogContextFqn.Equals(containingType.GetClrName()) && typeMember.ShortName == "PushProperty";
}
[CanBeNull]
public static IType GetFirstGenericArgumentType([NotNull]this IDeclaredType declared)
{
var substitution = declared.GetSubstitution();
var typeParameter = substitution.Domain.FirstOrDefault();
if (typeParameter == null)
{
return null;
}
return substitution.Apply(typeParameter);
}
private static bool IsVerbatimString([CanBeNull]this IExpression expression)
{
return expression?.FirstChild?.NodeType == CSharpTokenType.STRING_LITERAL_VERBATIM;
}
private static string GetExpressionText(this ICSharpExpression expression)
{
if (expression == null)
{
return null;
}
var stringLiteral = StringLiteralAltererUtil.TryCreateStringLiteralByExpression(expression);
if (stringLiteral == null)
{
return null;
}
var expressionText = stringLiteral.Expression.GetText();
if (expressionText.StartsWith("@"))
{
expressionText = expressionText.Substring(1);
}
return StringUtil.Unquote(expressionText);
}
private static void FlattenAdditiveExpression(IAdditiveExpression additiveExpression, LinkedList list)
{
foreach (var argumentInfo in additiveExpression.Arguments)
{
if (argumentInfo is ExpressionArgumentInfo expressionArgumentInfo && expressionArgumentInfo.Expression is IAdditiveExpression additive)
{
FlattenAdditiveExpression(additive, list);
continue;
}
list.AddLast((ExpressionArgumentInfo)argumentInfo);
}
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/AnonymousObjectWithoutDestructuringWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Models;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class AnonymousObjectDestructuringWarning : IHighlighting
{
private const string Message = "Anonymous objects must be destructured";
public const string SeverityId = "AnonymousObjectDestructuringProblem";
public AnonymousObjectDestructuringWarning(MessageTemplateTokenInformation tokenInformation)
{
TokenInformation = tokenInformation;
}
public string ErrorStripeToolTip => ToolTip;
public MessageTemplateTokenInformation TokenInformation { get; }
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return TokenInformation.DocumentRange;
}
public bool IsValid()
{
return TokenInformation.DocumentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/ComplexObjectDestructuringInContextWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class ComplexObjectDestructuringInContextWarning : ComplexObjectDestructuringWarningBase, IHighlighting
{
public const string SeverityId = "ComplexObjectInContextDestructuringProblem";
private readonly IInvocationExpression _invocationExpression;
public ComplexObjectDestructuringInContextWarning(IInvocationExpression invocationExpression)
{
_invocationExpression = invocationExpression;
}
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return _invocationExpression.GetDocumentRange();
}
public bool IsValid()
{
return _invocationExpression.GetDocumentRange().IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/ComplexObjectDestructuringWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Models;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class ComplexObjectDestructuringWarning : ComplexObjectDestructuringWarningBase, IHighlighting
{
public const string SeverityId = "ComplexObjectDestructuringProblem";
public ComplexObjectDestructuringWarning(MessageTemplateTokenInformation tokenInformation)
{
TokenInformation = tokenInformation;
}
public string ErrorStripeToolTip => ToolTip;
public MessageTemplateTokenInformation TokenInformation { get; }
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return TokenInformation.DocumentRange;
}
public bool IsValid()
{
return TokenInformation.DocumentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/ComplexObjectDestructuringWarningBase.cs
================================================
namespace ReSharper.Structured.Logging.Highlighting
{
public abstract class ComplexObjectDestructuringWarningBase
{
protected const string Message = "Complex objects with default ToString() implementation probably need to be destructured";
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/ContextualLoggerWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class ContextualLoggerWarning : IHighlighting
{
private const string Message = "Incorrect type is used for contextual logger";
public const string SeverityId = "ContextualLoggerProblem";
private readonly DocumentRange _range;
public ContextualLoggerWarning(DocumentRange documentRange)
{
_range = documentRange;
}
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return _range;
}
public bool IsValid()
{
return _range.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/DuplicateTemplatePropertyWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Models;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class DuplicateTemplatePropertyWarning : IHighlighting
{
private const string Message = "Duplicate properties in message template";
public const string SeverityId = "TemplateDuplicatePropertyProblem";
private readonly DocumentRange _documentRange;
public DuplicateTemplatePropertyWarning(MessageTemplateTokenInformation tokenInformation)
{
_documentRange = tokenInformation.DocumentRange;
}
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return _documentRange;
}
public bool IsValid()
{
return _documentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/ExceptionPassedAsTemplateArgumentWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class ExceptionPassedAsTemplateArgumentWarning : IHighlighting
{
public const string SeverityId = "ExceptionPassedAsTemplateArgumentProblem";
private const string Message = "Exception should be passed to the exception argument";
private readonly DocumentRange _documentRange;
public ExceptionPassedAsTemplateArgumentWarning(DocumentRange documentRange)
{
_documentRange = documentRange;
}
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return _documentRange;
}
public bool IsValid()
{
return _documentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/InconsistentContextLogPropertyNamingWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class InconsistentContextLogPropertyNamingWarning : InconsistentLogPropertyNamingWarningBase, IHighlighting
{
private readonly string _propertyName;
public const string SeverityId = "InconsistentContextLogPropertyNaming";
public InconsistentContextLogPropertyNamingWarning(ICSharpArgument argument, string propertyName, string suggestedName)
{
_propertyName = propertyName;
Argument = argument;
SuggestedName = suggestedName;
}
public string ErrorStripeToolTip => ToolTip;
public ICSharpArgument Argument { get; }
public string SuggestedName { get; }
public string ToolTip => GetToolTipMessage(_propertyName, SuggestedName);
public DocumentRange CalculateRange()
{
return Argument.GetDocumentRange();
}
public bool IsValid()
{
return Argument.GetDocumentRange().IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/InconsistentLogPropertyNamingWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Models;
using ReSharper.Structured.Logging.Serilog.Parsing;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class InconsistentLogPropertyNamingWarning : InconsistentLogPropertyNamingWarningBase, IHighlighting
{
public const string SeverityId = "InconsistentLogPropertyNaming";
public InconsistentLogPropertyNamingWarning(
MessageTemplateTokenInformation tokenInformation,
PropertyToken namedProperty,
string suggestedName)
{
TokenInformation = tokenInformation;
NamedProperty = namedProperty;
SuggestedName = suggestedName;
}
public string ErrorStripeToolTip => ToolTip;
public MessageTemplateTokenInformation TokenInformation { get; }
public PropertyToken NamedProperty { get; }
public string SuggestedName { get; }
public string ToolTip => GetToolTipMessage(NamedProperty.PropertyName, SuggestedName);
public DocumentRange CalculateRange()
{
return TokenInformation.DocumentRange;
}
public bool IsValid()
{
return TokenInformation.DocumentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/InconsistentLogPropertyNamingWarningBase.cs
================================================
namespace ReSharper.Structured.Logging.Highlighting
{
public class InconsistentLogPropertyNamingWarningBase
{
protected const string Message = "Property name '{0}' does not match naming rules. Suggested name is '{1}'.";
protected string GetToolTipMessage(string propertyName, string suggestedName)
{
return $"Property name '{propertyName}' does not match naming rules. Suggested name is '{suggestedName}'.";
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/LogMessageIsSentenceWarning.cs
================================================
using System.Text.RegularExpressions;
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Util;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class LogMessageIsSentenceWarning : IHighlighting
{
private const string Message = "Log event messages should be fragments, not sentences. Avoid a trailing period/full stop.";
public const string SeverityId = "LogMessageIsSentenceProblem";
private readonly DocumentRange _documentRange;
public LogMessageIsSentenceWarning(IStringLiteralAlterer stringLiteral, Regex regex)
{
StringLiteral = stringLiteral;
Regex = regex;
_documentRange = stringLiteral.Expression.GetDocumentRange();
}
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public IStringLiteralAlterer StringLiteral { get; }
public Regex Regex { get; }
public DocumentRange CalculateRange()
{
return _documentRange;
}
public bool IsValid()
{
return _documentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/PositionalPropertyUsedWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Models;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class PositionalPropertyUsedWarning : IHighlighting
{
private readonly MessageTemplateTokenInformation _tokenInformation;
private const string Message = "Prefer named properties instead of positional ones";
public const string SeverityId = "PositionalPropertyUsedProblem";
public PositionalPropertyUsedWarning(MessageTemplateTokenInformation tokenInformation)
{
_tokenInformation = tokenInformation;
}
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return _tokenInformation.DocumentRange;
}
public bool IsValid()
{
return _tokenInformation.DocumentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/TemplateFormatStringNonExistingArgumentWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class TemplateFormatStringNonExistingArgumentWarning : IHighlighting
{
private const string SeverityId = "TemplateFormatStringProblem";
private const string Message = "Non-existing argument in message template";
private readonly DocumentRange _documentRange;
public TemplateFormatStringNonExistingArgumentWarning(DocumentRange documentRange)
{
_documentRange = documentRange;
}
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return _documentRange;
}
public bool IsValid()
{
return _documentRange.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Highlighting/TemplateIsNotCompileTimeConstantWarning.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Highlighting
{
[RegisterConfigurableSeverity(
SeverityId,
null,
StructuredLoggingGroup.Id,
Message,
Message,
Severity.WARNING)]
[ConfigurableSeverityHighlighting(
SeverityId,
CSharpLanguage.Name,
OverlapResolve = OverlapResolveKind.WARNING,
ToolTipFormatString = Message)]
public class TemplateIsNotCompileTimeConstantWarning : IHighlighting
{
public const string SeverityId = "TemplateIsNotCompileTimeConstantProblem";
private const string Message = "Message template should be compile time constant";
public TemplateIsNotCompileTimeConstantWarning(
IInvocationExpression invocationExpression,
ICSharpArgument messageTemplateArgument)
{
InvocationExpression = invocationExpression;
MessageTemplateArgument = messageTemplateArgument;
}
public IInvocationExpression InvocationExpression { get; }
public ICSharpArgument MessageTemplateArgument { get; }
public string ErrorStripeToolTip => ToolTip;
public string ToolTip => Message;
public DocumentRange CalculateRange()
{
return MessageTemplateArgument.Expression.GetDocumentRange();
}
public bool IsValid()
{
return MessageTemplateArgument.IsValid();
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Models/MessageTemplateTokenInformation.cs
================================================
using JetBrains.DocumentModel;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Util;
namespace ReSharper.Structured.Logging.Models
{
public class MessageTemplateTokenInformation
{
public MessageTemplateTokenInformation(
DocumentRange documentRange,
IStringLiteralAlterer stringLiteral)
{
DocumentRange = documentRange;
StringLiteral = stringLiteral;
}
public DocumentRange DocumentRange { get; }
public IStringLiteralAlterer StringLiteral { get; }
public int RelativeStartIndex => DocumentRange.StartOffset - StringLiteral.Expression.GetDocumentRange().StartOffset - 1;
}
}
================================================
FILE: src/ReSharper.Structured.Logging/QuickFixes/AddDestructuringToMessageTemplatePropertyFix.cs
================================================
using System;
using JetBrains.Annotations;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.QuickFixes;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.Util;
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.TextControl;
using JetBrains.Util;
using ReSharper.Structured.Logging.Highlighting;
using ReSharper.Structured.Logging.Models;
namespace ReSharper.Structured.Logging.QuickFixes
{
[QuickFix]
public class AddDestructuringToMessageTemplatePropertyFix : QuickFixBase
{
private readonly MessageTemplateTokenInformation _tokenInformation;
public AddDestructuringToMessageTemplatePropertyFix([NotNull] AnonymousObjectDestructuringWarning error)
{
_tokenInformation = error.TokenInformation;
}
public AddDestructuringToMessageTemplatePropertyFix([NotNull] ComplexObjectDestructuringWarning error)
{
_tokenInformation = error.TokenInformation;
}
public override string Text => "Add destructuring to property";
public override bool IsAvailable(IUserDataHolder cache)
{
return _tokenInformation.DocumentRange.IsValid();
}
protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
{
using (WriteLockCookie.Create())
{
var factory = CSharpElementFactory.GetInstance(_tokenInformation.StringLiteral.Expression, false);
var startIndex = _tokenInformation.RelativeStartIndex;
var expression = factory.CreateExpression(
$"\"{_tokenInformation.StringLiteral.Expression.GetUnquotedText().Insert(startIndex + 1, "@")}\"");
// ReSharper disable once AssignNullToNotNullAttribute
ModificationUtil.ReplaceChild(_tokenInformation.StringLiteral.Expression, expression);
}
return null;
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/QuickFixes/RemoveTrailingPeriodFix.cs
================================================
using System;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.QuickFixes;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Util;
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.TextControl;
using JetBrains.Util;
using ReSharper.Structured.Logging.Highlighting;
namespace ReSharper.Structured.Logging.QuickFixes
{
[QuickFix]
public class RemoveTrailingPeriodFix : ScopedQuickFixBase
{
private readonly IStringLiteralAlterer _stringLiteral;
private readonly Regex _regex;
public RemoveTrailingPeriodFix([NotNull] LogMessageIsSentenceWarning error)
{
_stringLiteral = error.StringLiteral;
_regex = error.Regex;
}
public override string Text => "Remove period";
public override bool IsAvailable(IUserDataHolder cache)
{
return _stringLiteral.Expression.GetDocumentRange().IsValid();
}
///
protected override ITreeNode TryGetContextTreeNode()
{
return _stringLiteral.Expression;
}
protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
{
using (WriteLockCookie.Create())
{
var factory = CSharpElementFactory.GetInstance(_stringLiteral.Expression, false);
var expression = factory.CreateExpression(
$"\"{_regex.Replace(_stringLiteral.Expression.GetUnquotedText(), string.Empty)}\"");
ModificationUtil.ReplaceChild(_stringLiteral.Expression, expression);
}
return null;
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/QuickFixes/RenameContextLogPropertyFix.cs
================================================
using System;
using JetBrains.Annotations;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.QuickFixes;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.TextControl;
using JetBrains.Util;
using ReSharper.Structured.Logging.Highlighting;
namespace ReSharper.Structured.Logging.QuickFixes
{
[QuickFix]
public class RenameContextLogPropertyFix : QuickFixBase
{
private readonly ICSharpArgument _argument;
private readonly string _suggestedName;
public RenameContextLogPropertyFix([NotNull] InconsistentContextLogPropertyNamingWarning error)
{
_suggestedName = error.SuggestedName;
_argument = error.Argument;
}
public override string Text => $"Rename property to '{_suggestedName}'";
public override bool IsAvailable(IUserDataHolder cache)
{
return _argument.IsValid();
}
protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
{
using (WriteLockCookie.Create())
{
// ReSharper disable once AssignNullToNotNullAttribute
var factory = CSharpElementFactory.GetInstance(_argument.Expression, false);
var expression = factory.CreateExpression($"\"{_suggestedName}\"");
ModificationUtil.ReplaceChild(_argument.Expression, expression);
}
return null;
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/QuickFixes/RenameLogPropertyFix.cs
================================================
using System;
using JetBrains.Annotations;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.QuickFixes;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.Util;
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.TextControl;
using JetBrains.Util;
using ReSharper.Structured.Logging.Highlighting;
using ReSharper.Structured.Logging.Models;
using ReSharper.Structured.Logging.Serilog.Parsing;
namespace ReSharper.Structured.Logging.QuickFixes
{
[QuickFix]
public class RenameLogPropertyFix : QuickFixBase
{
private readonly PropertyToken _namedProperty;
private readonly MessageTemplateTokenInformation _tokenInformation;
private readonly string _suggestedName;
public RenameLogPropertyFix([NotNull] InconsistentLogPropertyNamingWarning error)
{
_namedProperty = error.NamedProperty;
_suggestedName = error.SuggestedName;
_tokenInformation = error.TokenInformation;
}
public override string Text => $"Rename property to '{_suggestedName}'";
public override bool IsAvailable(IUserDataHolder cache)
{
return _tokenInformation.DocumentRange.IsValid();
}
protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
{
using (WriteLockCookie.Create())
{
var factory = CSharpElementFactory.GetInstance(_tokenInformation.StringLiteral.Expression, false);
var relativeStartIndex = _tokenInformation.RelativeStartIndex;
var startIndex = _namedProperty.Destructuring == Destructuring.Default
? relativeStartIndex + 1
: relativeStartIndex + 2;
var length = _namedProperty.Destructuring == Destructuring.Default
? _namedProperty.Length - 2
: _namedProperty.Length - 3;
var expression = factory.CreateExpression(
$"\"{_tokenInformation.StringLiteral.Expression.GetUnquotedText().Remove(startIndex, length).Insert(startIndex, _suggestedName)}\"");
ModificationUtil.ReplaceChild(_tokenInformation.StringLiteral.Expression, expression);
}
return null;
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/QuickFixes/TemplateIsNotCompileTimeConstantFix.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using JetBrains.Application.Progress;
using JetBrains.DocumentModel;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.Bulbs;
using JetBrains.ReSharper.Feature.Services.LiveTemplates.Hotspots;
using JetBrains.ReSharper.Feature.Services.QuickFixes;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Parsing;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Naming;
using JetBrains.ReSharper.Psi.Naming.Elements;
using JetBrains.ReSharper.Psi.Naming.Extentions;
using JetBrains.ReSharper.Psi.Naming.Impl;
using JetBrains.ReSharper.Psi.Naming.Settings;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Resources.Shell;
using JetBrains.ReSharper.TestRunner.Abstractions.Extensions;
using JetBrains.TextControl;
using JetBrains.Util;
using ReSharper.Structured.Logging.Highlighting;
using ReSharper.Structured.Logging.Services;
namespace ReSharper.Structured.Logging.QuickFixes
{
[QuickFix]
public class TemplateIsNotCompileTimeConstantFix : QuickFixBase
{
public TemplateIsNotCompileTimeConstantFix([NotNull] TemplateIsNotCompileTimeConstantWarning error)
{
InvocationExpression = error.InvocationExpression;
MessageTemplateArgument = error.MessageTemplateArgument;
}
public override string Text => "Convert to compile-time constant message template";
public IInvocationExpression InvocationExpression { get; set; }
public ICSharpArgument MessageTemplateArgument { get; set; }
public override bool IsAvailable(IUserDataHolder cache)
{
return InvocationExpression.IsValid() && MessageTemplateArgument.Expression is IInterpolatedStringExpression;
}
// ReSharper disable once CognitiveComplexity
protected override Action ExecutePsiTransaction(ISolution solution, IProgressIndicator progress)
{
var interpolatedExpression = (IInterpolatedStringExpression)MessageTemplateArgument.Expression.NotNull();
var elementFactory = CSharpElementFactory.GetInstance(interpolatedExpression);
var hotspots = new List<(int Start, int End, List Suggestions)>();
var namingManager = solution.GetPsiServices().Naming;
var nameSuggestionManager = namingManager.Suggestion;
var namingLanguageService = NamingManager.GetNamingLanguageService(interpolatedExpression.Language);
var entryOptions = new EntryOptions(
PluralityKinds.Unknown,
SubrootPolicy.Decompose,
PredefinedPrefixPolicy.Remove);
var settingsStore = interpolatedExpression.GetSettingsStoreWithEditorConfig();
var suggestionOptions = new SuggestionOptions();
var sourceFile = interpolatedExpression.GetSourceFile()
.NotNull("interpolatedExpression.GetSourceFile() != null");
using (WriteLockCookie.Create())
{
var hotspotsRegistry = new HotspotsRegistry(interpolatedExpression.GetPsiServices());
var builder = new StringBuilder();
foreach (var treeNode in interpolatedExpression.Children())
{
if (treeNode is ITokenNode token)
{
if (treeNode.GetTokenType() == CSharpTokenType.INTERPOLATED_STRING_REGULAR_START)
builder.Append(token.GetText().Substring(2));
else if (treeNode.GetTokenType() == CSharpTokenType.INTERPOLATED_STRING_VERBATIM_START)
builder.Append(token.GetText().Substring(3));
else if (treeNode.GetTokenType() == CSharpTokenType.INTERPOLATED_STRING_REGULAR_END ||
treeNode.GetTokenType() == CSharpTokenType.INTERPOLATED_STRING_VERBATIM_END)
builder.Append(token.GetText().Substring(0, token.GetText().Length - 1));
else
builder.Append(token.GetText());
continue;
}
var insert = (IInterpolatedStringInsert)treeNode;
var namesCollection = nameSuggestionManager.CreateEmptyCollection(
PluralityKinds.Unknown,
treeNode.Language,
longerNamesFirst: true,
sourceFile);
var suggestRoots = namingLanguageService.SuggestRoots(
insert.Expression,
useExpectedTypes: false,
namesCollection.PolicyProvider);
foreach (var suggestRoot in suggestRoots)
namesCollection.Add(suggestRoot, entryOptions);
var defaultRule = namingManager.Policy.GetDefaultRule(
sourceFile,
interpolatedExpression.Language,
settingsStore,
NamedElementKinds.Property,
ElementKindOfElementType.PROPERTY);
var namesSuggestion = namesCollection.Prepare(defaultRule, ScopeKind.Common, suggestionOptions);
var firstName = PropertyNameProvider.GetSuggestedName(namesSuggestion.FirstName(), settingsStore);
hotspots.Add((
builder.Length + 1,
builder.Length + 1 + firstName.Length,
namesSuggestion.AllNames().Select(c => PropertyNameProvider.GetSuggestedName(c, settingsStore)).ToList()));
builder.Append(firstName);
var argument = elementFactory.CreateArgument(ParameterKind.VALUE, insert.Expression);
InvocationExpression.AddArgumentAfter(argument, InvocationExpression.Arguments.Last());
}
var literalExpression = elementFactory.CreateStringLiteralExpression(builder.ToString());
var literalExpressionArgument = InvocationExpression.AddArgumentAfter(
elementFactory.CreateArgument(ParameterKind.VALUE, literalExpression),
MessageTemplateArgument);
InvocationExpression.RemoveArgument(MessageTemplateArgument);
foreach (var (start, end, suggestions) in hotspots)
{
var documentRange = new DocumentRange(
literalExpressionArgument.GetDocumentStartOffset().Shift(start),
literalExpressionArgument.GetDocumentStartOffset().Shift(end));
hotspotsRegistry.Register(documentRange.CreateRangeMarker(), new TextHotspotExpression(suggestions));
}
var endSelectionRange = ExpressionStatementNavigator.GetByExpression(
InvocationExpressionNavigator.GetByArgument(literalExpressionArgument))
.GetDocumentEndOffset();
return BulbActionUtils.ExecuteHotspotSession(hotspotsRegistry, endSelectionRange);
}
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/ReSharper.Structured.Logging.Rider.csproj
================================================
net472
false
latest
true
false
ReSharper.Structured.Logging
================================================
FILE: src/ReSharper.Structured.Logging/ReSharper.Structured.Logging.csproj
================================================
net472
false
latest
false
bin\$(MSBuildProjectName)\$(Configuration)\
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Core/IMessageTemplateParser.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using ReSharper.Structured.Logging.Serilog.Events;
namespace ReSharper.Structured.Logging.Serilog.Core
{
public interface IMessageTemplateParser
{
MessageTemplate Parse(string messageTemplate);
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Events/MessageTemplate.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using ReSharper.Structured.Logging.Serilog.Parsing;
namespace ReSharper.Structured.Logging.Serilog.Events
{
///
/// Represents a message template passed to a log method. The template
/// can subsequently render the template in textual form given the list
/// of properties.
///
public class MessageTemplate
{
///
/// Represents the empty message template.
///
public static MessageTemplate Empty { get; } = new MessageTemplate(Enumerable.Empty());
readonly MessageTemplateToken[] _tokens;
///
/// Construct a message template using manually-defined text and property tokens.
///
/// The text and property tokens defining the template.
public MessageTemplate(IEnumerable tokens)
// ReSharper disable PossibleMultipleEnumeration
: this(string.Join("", tokens), tokens)
// ReSharper enable PossibleMultipleEnumeration
{
}
///
/// Construct a message template using manually-defined text and property tokens.
///
/// The full text of the template; used by Serilog internally to avoid unneeded
/// string concatenation.
/// The text and property tokens defining the template.
public MessageTemplate(string text, IEnumerable tokens)
{
if (text == null) throw new ArgumentNullException(nameof(text));
if (tokens == null) throw new ArgumentNullException(nameof(tokens));
Text = text;
_tokens = tokens.ToArray();
var propertyTokens = GetElementsOfTypeToArray(_tokens);
if (propertyTokens.Length != 0)
{
var allPositional = true;
var anyPositional = false;
foreach (var propertyToken in propertyTokens)
{
if (propertyToken.IsPositional)
anyPositional = true;
else
allPositional = false;
}
if (allPositional)
{
PositionalProperties = propertyTokens;
}
else
{
if (anyPositional)
IsMixedTemplate = true;
NamedProperties = propertyTokens;
}
}
}
///
/// Similar to , but faster.
///
static TResult[] GetElementsOfTypeToArray(MessageTemplateToken[] tokens)
where TResult: class
{
var result = new List(tokens.Length / 2);
for (var i = 0; i < tokens.Length; i++)
{
var token = tokens[i] as TResult;
if (token != null)
{
result.Add(token);
}
}
return result.ToArray();
}
///
/// The raw text describing the template.
///
public string Text { get; }
///
/// Render the template as a string.
///
/// The string representation of the template.
public override string ToString()
{
return Text;
}
///
/// The tokens parsed from the template.
///
public IEnumerable Tokens => _tokens;
internal MessageTemplateToken[] TokenArray => _tokens;
internal PropertyToken[] NamedProperties { get; }
internal PropertyToken[] PositionalProperties { get; }
public bool IsMixedTemplate { get; }
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Parsing/Alignment.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace ReSharper.Structured.Logging.Serilog.Parsing
{
///
/// A structure representing the alignment settings to apply when rendering a property.
///
public struct Alignment
{
///
/// Initializes a new instance of .
///
/// The text alignment direction.
/// The width of the text, in characters.
public Alignment(AlignmentDirection direction, int width)
{
Direction = direction;
Width = width;
}
///
/// The text alignment direction.
///
public AlignmentDirection Direction { get; }
///
/// The width of the text.
///
public int Width { get; }
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Parsing/AlignmentDirection.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace ReSharper.Structured.Logging.Serilog.Parsing
{
///
/// Defines the direction of the alignment.
///
public enum AlignmentDirection
{
///
/// Text will be left-aligned.
///
Left,
///
/// Text will be right-aligned.
///
Right
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Parsing/Destructuring.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace ReSharper.Structured.Logging.Serilog.Parsing
{
///
/// Instructs the logger on how to store information about provided
/// parameters.
///
public enum Destructuring
{
///
/// Convert known types and objects to scalars, arrays to sequences.
///
Default,
///
/// Convert all types to scalar strings. Prefix name with '$'.
///
Stringify,
///
/// Convert known types to scalars, destructure objects and collections
/// into sequences and structures. Prefix name with '@'.
///
Destructure
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Parsing/MessageTemplateParser.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Application.Parts;
using JetBrains.ProjectModel;
using ReSharper.Structured.Logging.Serilog.Core;
using ReSharper.Structured.Logging.Serilog.Events;
namespace ReSharper.Structured.Logging.Serilog.Parsing
{
///
/// Parses message template strings into sequences of text or property
/// tokens.
///
#if RIDER
[SolutionComponent]
#else
[SolutionComponent(Instantiation.DemandAnyThreadSafe)]
#endif
public class MessageTemplateParser : IMessageTemplateParser
{
///
/// Parse the supplied message template.
///
/// The message template to parse.
/// A sequence of text or property tokens. Where the template
/// is not syntactically valid, text tokens will be returned. The parser
/// will make a best effort to extract valid property tokens even in the
/// presence of parsing issues.
/// When is null
public MessageTemplate Parse(string messageTemplate)
{
if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate));
return new MessageTemplate(messageTemplate, Tokenize(messageTemplate));
}
static IEnumerable Tokenize(string messageTemplate)
{
if (messageTemplate.Length == 0)
{
yield return new TextToken("", 0);
yield break;
}
var nextIndex = 0;
while (true)
{
var beforeText = nextIndex;
var tt = ParseTextToken(nextIndex, messageTemplate, out nextIndex);
if (nextIndex > beforeText)
yield return tt;
if (nextIndex == messageTemplate.Length)
yield break;
var beforeProp = nextIndex;
var pt = ParsePropertyToken(nextIndex, messageTemplate, out nextIndex);
if (beforeProp < nextIndex)
yield return pt;
if (nextIndex == messageTemplate.Length)
yield break;
}
}
// ReSharper disable once CognitiveComplexity
static MessageTemplateToken ParsePropertyToken(int startAt, string messageTemplate, out int next)
{
var first = startAt;
startAt++;
while (startAt < messageTemplate.Length && IsValidInPropertyTag(messageTemplate[startAt]))
startAt++;
if (startAt == messageTemplate.Length || messageTemplate[startAt] != '}')
{
next = startAt;
return new TextToken(messageTemplate.Substring(first, next - first), first);
}
next = startAt + 1;
var rawText = messageTemplate.Substring(first, next - first);
var tagContent = rawText.Substring(1, next - (first + 2));
if (tagContent.Length == 0)
return new TextToken(rawText, first);
if (!TrySplitTagContent(tagContent, out var propertyNameAndDestructuring, out var format, out var alignment))
return new TextToken(rawText, first);
var propertyName = propertyNameAndDestructuring;
var destructuring = Destructuring.Default;
if (propertyName.Length != 0 && TryGetDestructuringHint(propertyName[0], out destructuring))
propertyName = propertyName.Substring(1);
if (propertyName.Length == 0)
return new TextToken(rawText, first);
for (var i = 0; i < propertyName.Length; ++i)
{
var c = propertyName[i];
if (!IsValidInPropertyName(c))
return new TextToken(rawText, first);
}
if (format != null)
{
for (var i = 0; i < format.Length; ++i)
{
var c = format[i];
if (!IsValidInFormat(c))
return new TextToken(rawText, first);
}
}
Alignment? alignmentValue = null;
if (alignment != null)
{
for (var i = 0; i < alignment.Length; ++i)
{
var c = alignment[i];
if (!IsValidInAlignment(c))
return new TextToken(rawText, first);
}
var lastDash = alignment.LastIndexOf('-');
if (lastDash > 0)
return new TextToken(rawText, first);
if (!int.TryParse(lastDash == -1 ? alignment : alignment.Substring(1), out var width) || width == 0)
return new TextToken(rawText, first);
var direction = lastDash == -1 ?
AlignmentDirection.Right :
AlignmentDirection.Left;
alignmentValue = new Alignment(direction, width);
}
return new PropertyToken(
propertyName,
rawText,
format,
alignmentValue,
destructuring,
first);
}
// ReSharper disable once CognitiveComplexity
static bool TrySplitTagContent(string tagContent, out string propertyNameAndDestructuring, out string format, out string alignment)
{
var formatDelim = tagContent.IndexOf(':');
var alignmentDelim = tagContent.IndexOf(',');
if (formatDelim == -1 && alignmentDelim == -1)
{
propertyNameAndDestructuring = tagContent;
format = null;
alignment = null;
}
else
{
if (alignmentDelim == -1 || (formatDelim != -1 && alignmentDelim > formatDelim))
{
propertyNameAndDestructuring = tagContent.Substring(0, formatDelim);
format = formatDelim == tagContent.Length - 1 ?
null :
tagContent.Substring(formatDelim + 1);
alignment = null;
}
else
{
propertyNameAndDestructuring = tagContent.Substring(0, alignmentDelim);
if (formatDelim == -1)
{
if (alignmentDelim == tagContent.Length - 1)
{
alignment = format = null;
return false;
}
format = null;
alignment = tagContent.Substring(alignmentDelim + 1);
}
else
{
if (alignmentDelim == formatDelim - 1)
{
alignment = format = null;
return false;
}
alignment = tagContent.Substring(alignmentDelim + 1, formatDelim - alignmentDelim - 1);
format = formatDelim == tagContent.Length - 1 ?
null :
tagContent.Substring(formatDelim + 1);
}
}
}
return true;
}
static bool IsValidInPropertyTag(char c)
{
return IsValidInDestructuringHint(c) ||
IsValidInPropertyName(c) ||
IsValidInFormat(c) ||
c == ':';
}
static bool IsValidInPropertyName(char c) => char.IsLetterOrDigit(c) || c == '_' || c == '.' || c == ' ';
static bool TryGetDestructuringHint(char c, out Destructuring destructuring)
{
switch (c)
{
case '@':
{
destructuring = Destructuring.Destructure;
return true;
}
case '$':
{
destructuring = Destructuring.Stringify;
return true;
}
default:
{
destructuring = Destructuring.Default;
return false;
}
}
}
static bool IsValidInDestructuringHint(char c)
{
return c == '@' ||
c == '$';
}
static bool IsValidInAlignment(char c)
{
return char.IsDigit(c) ||
c == '-';
}
static bool IsValidInFormat(char c)
{
return c != '}' &&
(char.IsLetterOrDigit(c) ||
char.IsPunctuation(c) ||
c == ' ' ||
c == '+');
}
// ReSharper disable once CognitiveComplexity
private static TextToken ParseTextToken(int startAt, string messageTemplate, out int next)
{
var first = startAt;
var accum = new StringBuilder();
do
{
var nc = messageTemplate[startAt];
if (nc == '{')
{
if (startAt + 1 < messageTemplate.Length &&
messageTemplate[startAt + 1] == '{')
{
accum.Append(nc);
startAt++;
}
else
{
break;
}
}
else
{
accum.Append(nc);
if (nc == '}')
{
if (startAt + 1 < messageTemplate.Length &&
messageTemplate[startAt + 1] == '}')
{
startAt++;
}
}
}
startAt++;
} while (startAt < messageTemplate.Length);
next = startAt;
return new TextToken(accum.ToString(), first);
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Parsing/MessageTemplateToken.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace ReSharper.Structured.Logging.Serilog.Parsing
{
///
/// An element parsed from a message template string.
///
public abstract class MessageTemplateToken
{
///
/// Construct a .
///
/// The token's start index in the template.
protected MessageTemplateToken(int startIndex)
{
StartIndex = startIndex;
}
///
/// The token's start index in the template.
///
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public int StartIndex { get; }
///
/// The token's length.
///
public abstract int Length { get; }
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Parsing/PropertyToken.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.ComponentModel;
using System.Globalization;
namespace ReSharper.Structured.Logging.Serilog.Parsing
{
///
/// A message template token representing a log event property.
///
public sealed class PropertyToken : MessageTemplateToken
{
readonly string _rawText;
readonly int? _position;
///
/// Construct a .
///
/// The name of the property.
/// The token as it appears in the message template.
/// The format applied to the property, if any.
/// The destructuring strategy applied to the property, if any.
///
[Obsolete("Use named arguments with this method to guarantee forwards-compatibility."), EditorBrowsable(EditorBrowsableState.Never)]
public PropertyToken(string propertyName, string rawText, string formatObsolete, Destructuring destructuringObsolete)
: this(propertyName, rawText, formatObsolete, null, destructuringObsolete)
{
}
///
/// Construct a .
///
/// The name of the property.
/// The token as it appears in the message template.
/// The format applied to the property, if any.
/// The alignment applied to the property, if any.
/// The destructuring strategy applied to the property, if any.
/// The token's start index in the template.
///
public PropertyToken(string propertyName, string rawText, string format = null, Alignment? alignment = null, Destructuring destructuring = Destructuring.Default, int startIndex = -1)
: base(startIndex)
{
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
Format = format;
Destructuring = destructuring;
_rawText = rawText ?? throw new ArgumentNullException(nameof(rawText));
Alignment = alignment;
int position;
if (int.TryParse(PropertyName, NumberStyles.None, CultureInfo.InvariantCulture, out position) &&
position >= 0)
{
_position = position;
}
}
///
/// The token's length.
///
public override int Length => _rawText.Length;
///
/// The property name.
///
public string PropertyName { get; }
///
/// Destructuring strategy applied to the property.
///
public Destructuring Destructuring { get; }
///
/// Format applied to the property.
///
public string Format { get; }
///
/// Alignment applied to the property.
///
public Alignment? Alignment { get; }
///
/// True if the property name is a positional index; otherwise, false.
///
public bool IsPositional => _position.HasValue;
internal string RawText => _rawText;
///
/// Try to get the integer value represented by the property name.
///
/// The integer value, if present.
/// True if the property is positional, otherwise false.
public bool TryGetPositionalValue(out int position)
{
if (_position == null)
{
position = 0;
return false;
}
position = _position.Value;
return true;
}
///
/// Determines whether the specified is equal to the current .
///
///
/// true if the specified object is equal to the current object; otherwise, false.
///
/// The object to compare with the current object. 2
public override bool Equals(object obj)
{
var pt = obj as PropertyToken;
return pt != null &&
pt.Destructuring == Destructuring &&
pt.Format == Format &&
pt.PropertyName == PropertyName &&
pt._rawText == _rawText;
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
/// 2
public override int GetHashCode() => PropertyName.GetHashCode();
///
/// Returns a string that represents the current object.
///
///
/// A string that represents the current object.
///
/// 2
public override string ToString() => _rawText;
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Serilog/Parsing/TextToken.cs
================================================
// Copyright 2013-2015 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
namespace ReSharper.Structured.Logging.Serilog.Parsing
{
///
/// A message template token representing literal text.
///
public sealed class TextToken : MessageTemplateToken
{
///
/// Construct a .
///
/// The text of the token.
/// The token's start index in the template.
///
public TextToken(string text, int startIndex = -1) : base(startIndex)
{
Text = text ?? throw new ArgumentNullException(nameof(text));
}
///
/// The token's length.
///
public override int Length => Text.Length;
///
/// Determines whether the specified is equal to the current .
///
///
/// true if the specified object is equal to the current object; otherwise, false.
///
/// The object to compare with the current object. 2
public override bool Equals(object obj)
{
var tt = obj as TextToken;
return tt != null && tt.Text == Text;
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
/// 2
public override int GetHashCode() => Text.GetHashCode();
///
/// Returns a string that represents the current object.
///
///
/// A string that represents the current object.
///
/// 2
public override string ToString() => Text;
///
/// The text of the token.
///
public string Text { get; }
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Settings/PropertyNamingType.cs
================================================
namespace ReSharper.Structured.Logging.Settings
{
public enum PropertyNamingType
{
PascalCase,
CamelCase,
SnakeCase,
///
/// The elastic naming convention.
///
///
/// https://www.elastic.co/guide/en/beats/devguide/current/event-conventions.html
///
ElasticNaming
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Settings/StructuredLoggingGroup.cs
================================================
using JetBrains.ReSharper.Feature.Services.Daemon;
namespace ReSharper.Structured.Logging.Settings
{
[RegisterConfigurableHighlightingsGroup(Id, Name)]
public static class StructuredLoggingGroup
{
public const string Id = "StructuredLogging";
private const string Name = "Structured Logging Misuse";
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Settings/StructuredLoggingOptionsPage.cs
================================================
using System;
using JetBrains.Application.Settings;
using JetBrains.Application.UI.Options;
using JetBrains.Application.UI.Options.OptionPages;
using JetBrains.Application.UI.Options.OptionsDialog;
using JetBrains.IDE.UI.Extensions;
using JetBrains.IDE.UI.Options;
using JetBrains.Lifetimes;
using JetBrains.ReSharper.Feature.Services.Resources;
namespace ReSharper.Structured.Logging.Settings
{
[OptionsPage(Pid, "Structured Logging", typeof(FeaturesEnvironmentOptionsThemedIcons.StringFormat), ParentId = EnvironmentPage.Pid)]
public class StructuredLoggingOptionsPage : BeSimpleOptionsPage
{
private const string Pid = "StructuredLogging";
public StructuredLoggingOptionsPage(
Lifetime lifetime,
OptionsPageContext optionsPageContext,
OptionsSettingsSmartContext optionsSettingsSmartContext,
bool wrapInScrollablePanel = false)
: base(lifetime, optionsPageContext, optionsSettingsSmartContext, wrapInScrollablePanel)
{
AddHeader("Log properties naming style");
AddComboOptionFromEnum(
(StructuredLoggingSettings settings) => settings.PropertyNamingType,
type =>
{
switch (type)
{
case PropertyNamingType.PascalCase:
return "PascalCase";
case PropertyNamingType.CamelCase:
return "camelCase";
case PropertyNamingType.SnakeCase:
return "snake_case";
case PropertyNamingType.ElasticNaming:
return "elastic.naming";
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
});
AddHeader("Ignored properties naming");
AddCommentText("You may specify a regular expression here and if a property matches this expression, then it will be skipped during naming analysis.");
var ignoredRegex = OptionsSettingsSmartContext
.GetValueProperty(lifetime, StructuredLoggingSettingsAccessor.IgnoredPropertiesRegex);
AddControl(ignoredRegex.GetBeTextBox(lifetime));
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Settings/StructuredLoggingSettings.cs
================================================
using JetBrains.Application.Settings;
using JetBrains.Application.Settings.WellKnownRootKeys;
namespace ReSharper.Structured.Logging.Settings
{
[SettingsKey(typeof(EnvironmentSettings), "Settings for Structured Logging")]
public class StructuredLoggingSettings
{
[SettingsEntry(PropertyNamingType.PascalCase, "Properties naming case")]
public PropertyNamingType PropertyNamingType { get; set; }
[SettingsEntry("", "Ignored properties RegEx")]
public string IgnoredPropertiesRegex { get; set; }
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Settings/StructuredLoggingSettingsAccessor.cs
================================================
using System;
using System.Linq.Expressions;
using JetBrains.Annotations;
namespace ReSharper.Structured.Logging.Settings
{
public static class StructuredLoggingSettingsAccessor
{
[NotNull]
public static readonly Expression> PropertyNamingType = x => x.PropertyNamingType;
[NotNull]
public static readonly Expression> IgnoredPropertiesRegex = x => x.IgnoredPropertiesRegex;
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Utils/PropertyNameProvider.cs
================================================
using JetBrains.Annotations;
using JetBrains.Application.Settings;
using JetBrains.Util;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Services;
public static class PropertyNameProvider
{
public static string GetSuggestedName([NotNull]string propertyName, [CanBeNull]IContextBoundSettingsStore settingsStore)
{
var namingType = settingsStore
?.GetValue(StructuredLoggingSettingsAccessor.PropertyNamingType)
?? PropertyNamingType.PascalCase;
switch (namingType)
{
case PropertyNamingType.PascalCase:
return StringUtil.MakeUpperCamelCaseName(propertyName);
case PropertyNamingType.CamelCase:
return StringUtil.MakeUpperCamelCaseName(propertyName).Decapitalize();
case PropertyNamingType.SnakeCase:
return StringUtil.MakeUnderscoreCaseName(propertyName);
case PropertyNamingType.ElasticNaming:
return StringUtil.MakeUnderscoreCaseName(propertyName).Replace('_', '.');
default:
return propertyName;
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/Wiki/StructuredLoggingWikiDataProvider.cs
================================================
using System.Collections.Generic;
using JetBrains.Application;
using JetBrains.Application.Parts;
using JetBrains.ReSharper.Feature.Services.Explanatory;
using ReSharper.Structured.Logging.Highlighting;
namespace ReSharper.Structured.Logging.Wiki
{
[ShellComponent(Instantiation.DemandAnyThreadSafe)]
public class StructuredLoggingWikiDataProvider : ICodeInspectionWikiDataProvider
{
private static readonly IDictionary AttributeUrlMap = new Dictionary
{
{
DuplicateTemplatePropertyWarning.SeverityId,
CreateSeverityUrl(DuplicateTemplatePropertyWarning.SeverityId)
},
{
ExceptionPassedAsTemplateArgumentWarning.SeverityId,
CreateSeverityUrl(ExceptionPassedAsTemplateArgumentWarning.SeverityId)
},
{
TemplateIsNotCompileTimeConstantWarning.SeverityId,
CreateSeverityUrl(TemplateIsNotCompileTimeConstantWarning.SeverityId)
},
{
AnonymousObjectDestructuringWarning.SeverityId,
CreateSeverityUrl(AnonymousObjectDestructuringWarning.SeverityId)
},
{
ContextualLoggerWarning.SeverityId,
CreateSeverityUrl(ContextualLoggerWarning.SeverityId)
},
{
ComplexObjectDestructuringWarning.SeverityId,
CreateSeverityUrl(ComplexObjectDestructuringWarning.SeverityId)
},
{
PositionalPropertyUsedWarning.SeverityId,
CreateSeverityUrl(PositionalPropertyUsedWarning.SeverityId)
},
{
InconsistentLogPropertyNamingWarning.SeverityId,
CreateSeverityUrl(InconsistentLogPropertyNamingWarning.SeverityId)
},
{
LogMessageIsSentenceWarning.SeverityId,
CreateSeverityUrl(LogMessageIsSentenceWarning.SeverityId)
},
{
ComplexObjectDestructuringInContextWarning.SeverityId,
CreateSeverityUrl(ComplexObjectDestructuringInContextWarning.SeverityId)
},
{
InconsistentContextLogPropertyNamingWarning.SeverityId,
CreateSeverityUrl(InconsistentContextLogPropertyNamingWarning.SeverityId)
}
};
public bool TryGetValue(string attributeId, out string url)
{
return AttributeUrlMap.TryGetValue(attributeId, out url);
}
private static string CreateSeverityUrl(string severityId)
{
return $"https://github.com/olsh/resharper-structured-logging/blob/master/rules/{severityId}.md";
}
}
}
================================================
FILE: src/ReSharper.Structured.Logging/ZoneMarker.cs
================================================
using JetBrains.Application.BuildScript.Application.Zones;
using JetBrains.ReSharper.Psi.CSharp;
namespace ReSharper.Structured.Logging
{
[ZoneMarker]
public class ZoneMarker : IRequire
{
}
}
================================================
FILE: src/ReSharper.Structured.Logging/app.config
================================================
================================================
FILE: src/ReSharper.Structured.Logging.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32126.317
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReSharper.Structured.Logging", "ReSharper.Structured.Logging\ReSharper.Structured.Logging.csproj", "{0B29307F-4E38-4611-BA0D-408A4B4F4E15}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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
..\.editorconfig = ..\.editorconfig
..\appveyor.yml = ..\appveyor.yml
..\build.gradle = ..\build.gradle
..\Directory.Build.props = ..\Directory.Build.props
rider\main\resources\META-INF\plugin.xml = rider\main\resources\META-INF\plugin.xml
..\README.md = ..\README.md
..\.gitignore = ..\.gitignore
..\.github\dependabot.yml = ..\.github\dependabot.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReSharper.Structured.Logging.Rider", "ReSharper.Structured.Logging\ReSharper.Structured.Logging.Rider.csproj", "{81C42342-6FD5-48D0-B45F-FD364B170FC1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReSharper.Structured.Logging.Rider.Tests", "..\test\src\ReSharper.Structured.Logging.Rider.Tests.csproj", "{6BC99E0B-6362-4A62-84F1-103B93896294}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rules", "Rules", "{D93C6901-5685-495B-A790-6C1467978205}"
ProjectSection(SolutionItems) = preProject
..\rules\AnonymousObjectDestructuringProblem.md = ..\rules\AnonymousObjectDestructuringProblem.md
..\rules\ComplexObjectDestructuringProblem.md = ..\rules\ComplexObjectDestructuringProblem.md
..\rules\ComplexObjectInContextDestructuringProblem.md = ..\rules\ComplexObjectInContextDestructuringProblem.md
..\rules\ContextualLoggerProblem.md = ..\rules\ContextualLoggerProblem.md
..\rules\ExceptionPassedAsTemplateArgumentProblem.md = ..\rules\ExceptionPassedAsTemplateArgumentProblem.md
..\rules\InconsistentContextLogPropertyNaming.md = ..\rules\InconsistentContextLogPropertyNaming.md
..\rules\InconsistentLogPropertyNaming.md = ..\rules\InconsistentLogPropertyNaming.md
..\rules\LogMessageIsSentenceProblem.md = ..\rules\LogMessageIsSentenceProblem.md
..\rules\PositionalPropertyUsedProblem.md = ..\rules\PositionalPropertyUsedProblem.md
..\rules\TemplateDuplicatePropertyProblem.md = ..\rules\TemplateDuplicatePropertyProblem.md
..\rules\TemplateIsNotCompileTimeConstantProblem.md = ..\rules\TemplateIsNotCompileTimeConstantProblem.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "..\build\_build.csproj", "{A7717BDB-133B-433E-97CF-624B746FDDC8}"
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
{81C42342-6FD5-48D0-B45F-FD364B170FC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81C42342-6FD5-48D0-B45F-FD364B170FC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81C42342-6FD5-48D0-B45F-FD364B170FC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81C42342-6FD5-48D0-B45F-FD364B170FC1}.Release|Any CPU.Build.0 = Release|Any CPU
{6BC99E0B-6362-4A62-84F1-103B93896294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6BC99E0B-6362-4A62-84F1-103B93896294}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BC99E0B-6362-4A62-84F1-103B93896294}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6BC99E0B-6362-4A62-84F1-103B93896294}.Release|Any CPU.Build.0 = Release|Any CPU
{A7717BDB-133B-433E-97CF-624B746FDDC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7717BDB-133B-433E-97CF-624B746FDDC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2861CBD1-55F1-4AF9-8F6D-F2C345E8BB03}
EndGlobalSection
EndGlobal
================================================
FILE: src/rider/main/kotlin/com/jetbrains/rider/settings/StructuredLoggingBundle.kt
================================================
package com.jetbrains.rider.settings
import com.intellij.DynamicBundle
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.PropertyKey
class StructuredLoggingBundle : DynamicBundle(BUNDLE) {
companion object {
@NonNls
private const val BUNDLE = "messages.StructuredLoggingBundle"
private val INSTANCE: StructuredLoggingBundle = StructuredLoggingBundle()
@Nls
fun message(
@PropertyKey(resourceBundle = BUNDLE) key: String,
vararg params: Any
): String {
return INSTANCE.getMessage(key, *params)
}
}
}
================================================
FILE: src/rider/main/kotlin/com/jetbrains/rider/settings/StructuredLoggingPluginOptionsPage.kt
================================================
package com.jetbrains.rider.settings
import com.jetbrains.rider.settings.simple.SimpleOptionsPage
import com.jetbrains.rider.settings.StructuredLoggingBundle
class StructuredLoggingPluginOptionsPage : SimpleOptionsPage(
name = StructuredLoggingBundle.message("configurable.name.structuredlogging.title"),
pageId = "StructuredLogging")
{
override fun getId(): String {
return "StructuredLogging"
}
}
================================================
FILE: src/rider/main/resources/META-INF/plugin.xml
================================================
com.intellij.resharper.StructuredLogging
Structured Logging
For more information visit the project repository.
Release notes
]]>
https://github.com/olsh/resharper-structured-logging/releases
Oleg Shevchenko
messages.StructuredLoggingBundle
com.intellij.modules.rider
================================================
FILE: src/rider/main/resources/messages/StructuredLoggingBundle.properties
================================================
configurable.name.structuredlogging.title=Structured Logging
================================================
FILE: test/data/Analyzers/AnonymousTypeDestructure/SerilogWithComplexPropertyWithoutDestructure.cs
================================================
using Serilog;
using System;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyProperty}", new { Test = 1, Complex = new Random() });
}
}
}
================================================
FILE: test/data/Analyzers/AnonymousTypeDestructure/SerilogWithComplexPropertyWithoutDestructure.cs.gold
================================================
using Serilog;
using System;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{MyProperty}|(0)", new { Test = 1, Complex = new Random() });
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Anonymous objects must be destructured
================================================
FILE: test/data/Analyzers/AnonymousTypeDestructure/SerilogWithoutDestructure.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyProperty}", new { Test = 1 });
}
}
}
================================================
FILE: test/data/Analyzers/AnonymousTypeDestructure/SerilogWithoutDestructure.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{MyProperty}|(0)", new { Test = 1 });
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Anonymous objects must be destructured
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogContextExplicitDestructure.cs
================================================
using System;
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("Test", new Random(), true);
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogContextExplicitDestructure.cs.gold
================================================
using System;
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("Test", new Random(), true);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogContextNumericWithoutDestructure.cs
================================================
using System;
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("Test", 1);
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogContextNumericWithoutDestructure.cs.gold
================================================
using System;
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("Test", 1);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogContextWithoutDestructure.cs
================================================
using System;
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("Test", new Random());
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogContextWithoutDestructure.cs.gold
================================================
using System;
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
|LogContext.PushProperty("Test", new Random())|(0);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Complex objects with default ToString() implementation probably need to be destructured
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogCustomExceptionWithoutDestructure.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Error(new MyException(), "{MyProperty}", new Random());
}
}
public class MyException : Exception
{
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogCustomExceptionWithoutDestructure.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Error(new MyException(), "|{MyProperty}|(0)", new Random());
}
}
public class MyException : Exception
{
}
}
---------------------------------------------------------
(0): ReSharper Warning: Complex objects with default ToString() implementation probably need to be destructured
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogDictionaryWithoutDestructure.cs
================================================
using System;
using System.Collections.Generic;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{$MyProperty}", new Dictionary());
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogDictionaryWithoutDestructure.cs.gold
================================================
using System;
using System.Collections.Generic;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{$MyProperty}", new Dictionary());
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogEnumerableWithoutDestructure.cs
================================================
using System;
using System.Collections.Generic;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
IEnumerable list = new List() { "test" };
Log.Logger.Information("{MyProperty}", list);
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogEnumerableWithoutDestructure.cs.gold
================================================
using System;
using System.Collections.Generic;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
IEnumerable list = new List() { "test" };
Log.Logger.Information("{MyProperty}", list);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogForceStringWithoutDestructure.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{$MyProperty}", new Random());
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogForceStringWithoutDestructure.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{$MyProperty}", new Random());
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogNullableWithoutDestructure.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
int? a = 1;
Log.Logger.Information("{$MyProperty}", a);
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogNullableWithoutDestructure.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
int? a = 1;
Log.Logger.Information("{$MyProperty}", a);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogNumericWithoutDestructure.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{$MyProperty}", 3);
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogNumericWithoutDestructure.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{$MyProperty}", 3);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogParentWithOverriddenToString.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyProperty}", new B());
}
}
public class A
{
public override string ToString() => "Custom ToString";
}
public class B: A { }
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogParentWithOverriddenToString.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyProperty}", new B());
}
}
public class A
{
public override string ToString() => "Custom ToString";
}
public class B: A { }
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogWithoutDestructure.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyProperty}", new Random());
}
}
}
================================================
FILE: test/data/Analyzers/ComplexTypeDestructure/SerilogWithoutDestructure.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{MyProperty}|(0)", new Random());
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Complex objects with default ToString() implementation probably need to be destructured
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftCorrectContextType.cs
================================================
using Microsoft.Extensions.Logging;
class A
{
ILogger _log;
public A(ILogger log)
{
_log = log;
}
}
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftCorrectContextType.cs.gold
================================================
using Microsoft.Extensions.Logging;
class A
{
ILogger _log;
public A(ILogger log)
{
_log = log;
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextType.cs
================================================
using Microsoft.Extensions.Logging;
class A
{
ILogger _log;
public A(ILogger log)
{
_log = log;
}
}
class B { }
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextType.cs.gold
================================================
using Microsoft.Extensions.Logging;
class A
{
ILogger _log;
public A(|ILogger|(0) log)
{
_log = log;
}
}
class B { }
---------------------------------------------------------
(0): ReSharper Warning: Incorrect type is used for contextual logger
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleNamespaces.cs
================================================
using Microsoft.Extensions.Logging;
namespace X
{
class A { }
}
namespace Y
{
class A
{
ILogger _log;
public A(ILogger log)
{
_log = log;
}
}
}
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleNamespaces.cs.gold
================================================
using Microsoft.Extensions.Logging;
namespace X
{
class A { }
}
namespace Y
{
class A
{
ILogger _log;
public A(|ILogger|(0) log)
{
_log = log;
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Incorrect type is used for contextual logger
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleParameters.cs
================================================
using Microsoft.Extensions.Logging;
class A
{
ILogger _log;
public A(int a, ILogger log)
{
_log = log;
}
}
class B { }
================================================
FILE: test/data/Analyzers/ContextualLoggerConstructor/MicrosoftWrongContextTypeMultipleParameters.cs.gold
================================================
using Microsoft.Extensions.Logging;
class A
{
ILogger _log;
public A(int a, |ILogger|(0) log)
{
_log = log;
}
}
class B { }
---------------------------------------------------------
(0): ReSharper Warning: Incorrect type is used for contextual logger
================================================
FILE: test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogCorrectContextType.cs
================================================
using Serilog;
class A
{
private static readonly ILogger Logger = Logger.ForContext();
}
class B {}
================================================
FILE: test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogCorrectContextType.cs.gold
================================================
using Serilog;
class A
{
private static readonly ILogger Logger = Logger.ForContext();
}
class B {}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogWrongContextType.cs
================================================
using Serilog;
class A
{
private static readonly ILogger Logger = Logger.ForContext();
}
class B {}
================================================
FILE: test/data/Analyzers/ContextualLoggerSerilogFactory/SerilogWrongContextType.cs.gold
================================================
using Serilog;
class A
{
private static readonly ILogger Logger = |Logger.ForContext()|(0);
}
class B {}
---------------------------------------------------------
(0): ReSharper Warning: Incorrect type is used for contextual logger
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogCorrectExceptionPassing.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information(new Exception(), "{One}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogCorrectExceptionPassing.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information(new Exception(), "{One}", 1);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassing.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{One} {Exc}", 1, new Exception());
}
}
}
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassing.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{One} {Exc}", 1, |new Exception()|(0));
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Exception should be passed to the exception argument
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassingDynamicTemplate.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information($"{DateTime.Now} {{Error}}", new Exception());
}
}
}
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogIncorrectExceptionPassingDynamicTemplate.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information($"{DateTime.Now} |{{|(0)Error|}}|(1)", |new Exception()|(2));
}
}
}
---------------------------------------------------------
(0): ReSharper C# Escape Character 1:
(1): ReSharper C# Escape Character 1:
(2): ReSharper Warning: Exception should be passed to the exception argument
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogMultipleExceptionPassing.cs
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information(new Exception(), "{One} {OtherException}", 1, new Exception());
}
}
}
================================================
FILE: test/data/Analyzers/CorrectExceptionPassing/SerilogMultipleExceptionPassing.cs.gold
================================================
using System;
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information(new Exception(), "{One} {OtherException}", 1, new Exception());
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/DuplicatePropertiesTemplate/SerilogDuplicateNamedProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{Test} {Test}", 1, 2);
}
}
}
================================================
FILE: test/data/Analyzers/DuplicatePropertiesTemplate/SerilogDuplicateNamedProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{Test}|(0) |{Test}|(1)", 1, 2);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Duplicate properties in message template
(1): ReSharper Warning: Duplicate properties in message template
================================================
FILE: test/data/Analyzers/LogMessageIsSentence/SerilogNotSentenceMessage.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Loading {Name}...", "World");
}
}
}
================================================
FILE: test/data/Analyzers/LogMessageIsSentence/SerilogNotSentenceMessage.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Loading {Name}...", "World");
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/LogMessageIsSentence/SerilogSentenceMessage.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Hello {Name}.", "World");
}
}
}
================================================
FILE: test/data/Analyzers/LogMessageIsSentence/SerilogSentenceMessage.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information(|"Hello {Name}."|(0), "World");
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Log event messages should be fragments, not sentences. Avoid a trailing period/full stop.
================================================
FILE: test/data/Analyzers/PositionalPropertiesUsage/SerilogPositionProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{0}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PositionalPropertiesUsage/SerilogPositionProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{0}|(0)", 1);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Prefer named properties instead of positional ones
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInterpolatedStringProperty.cs
================================================
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
var s = "world";
LogContext.PushProperty($"Hello{s}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInterpolatedStringProperty.cs.gold
================================================
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
var s = "world";
LogContext.PushProperty($"Hello{s}", 1);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInvalidNamedProperty.cs
================================================
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("test", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogContextInvalidNamedProperty.cs.gold
================================================
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty(|"test"|(0), 1);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Property name 'test' does not match naming rules. Suggested name is 'Test'.
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogIgnoredInvalidNamedProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MY_IGNORED.Property_}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogIgnoredInvalidNamedProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MY_IGNORED.Property_}", 1);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidElasticNamedProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{myProperty}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidElasticNamedProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{myProperty}|(0)", 1);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Property name 'myProperty' does not match naming rules. Suggested name is 'my.property'.
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{myProperty}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{myProperty}|(0)", 1);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Property name 'myProperty' does not match naming rules. Suggested name is 'MyProperty'.
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithDot.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{My.Property}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithDot.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{My.Property}|(0)", 1);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Property name 'My.Property' does not match naming rules. Suggested name is 'MyProperty'.
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithSpace.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{My Property}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidNamedPropertyWithSpace.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("|{My Property}|(0)", 1);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Property name 'My Property' does not match naming rules. Suggested name is 'MyProperty'.
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidSyntax.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information(%"{MyProperty}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogInvalidSyntax.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information(%"{MyProperty}", 1);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidDestructuredNamedProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{@MyProperty}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidDestructuredNamedProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{@MyProperty}", 1);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidNamedProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyProperty}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzer/SerilogValidNamedProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyProperty}", 1);
}
}
}
---------------------------------------------------------
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzerDotNet6/ZLoggerInvalidNamedProperty.cs
================================================
using Microsoft.Extensions.Logging;
using ZLogger;
namespace ConsoleApp
{
class A
{
public A(ILogger log)
{
log.ZLogInformation("{myProperty}", 1);
}
}
}
================================================
FILE: test/data/Analyzers/PropertiesNamingAnalyzerDotNet6/ZLoggerInvalidNamedProperty.cs.gold
================================================
using Microsoft.Extensions.Logging;
using ZLogger;
namespace ConsoleApp
{
class A
{
public A(ILogger log)
{
log.ZLogInformation("|{myProperty}|(0)", 1);
}
}
}
---------------------------------------------------------
(0): ReSharper Warning: Property name 'myProperty' does not match naming rules. Suggested name is 'MyProperty'.
================================================
FILE: test/data/QuickFixes/AddDestructuringFix/SerilogEscapedString.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Escaped \r\n {MyPro{caret}perty} \r\n string", new { Test = 1 });
}
}
}
================================================
FILE: test/data/QuickFixes/AddDestructuringFix/SerilogEscapedString.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Escaped \r\n {@MyPro{caret}perty} \r\n string", new { Test = 1 });
}
}
}
================================================
FILE: test/data/QuickFixes/AddDestructuringFix/SerilogNewAnonymousObject.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyPro{caret}perty}", new { Test = 1 });
}
}
}
================================================
FILE: test/data/QuickFixes/AddDestructuringFix/SerilogNewAnonymousObject.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{@MyPro{caret}perty}", new { Test = 1 });
}
}
}
================================================
FILE: test/data/QuickFixes/AddDestructuringFix/SerilogNewComplexObject.cs
================================================
using Serilog;
using System;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{MyPro{caret}perty}", new Random());
}
}
}
================================================
FILE: test/data/QuickFixes/AddDestructuringFix/SerilogNewComplexObject.cs.gold
================================================
using Serilog;
using System;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("{@MyPro{caret}perty}", new Random());
}
}
}
================================================
FILE: test/data/QuickFixes/RemoveTrailingPeriodFix/SerilogTrailingPeriod.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test {caret}{Property} prop.", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RemoveTrailingPeriodFix/SerilogTrailingPeriod.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test {caret}{Property} prop", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameContextLogPropertyFix/SerilogContextProperty.cs
================================================
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("{caret}test", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameContextLogPropertyFix/SerilogContextProperty.cs.gold
================================================
using Serilog;
using Serilog.Context;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
LogContext.PushProperty("T{caret}est", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameLogPropertyFix/SerilogDestructuredProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test {@my{caret}Property} prop", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameLogPropertyFix/SerilogDestructuredProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test {@My{caret}Property} prop", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameLogPropertyFix/SerilogProperty.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test {my{caret}Property} prop", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameLogPropertyFix/SerilogProperty.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test {My{caret}Property} prop", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameLogPropertyFix/SerilogPropertyConcatenated.cs
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test" + " {my{caret}Property} prop", 1);
}
}
}
================================================
FILE: test/data/QuickFixes/RenameLogPropertyFix/SerilogPropertyConcatenated.cs.gold
================================================
using Serilog;
namespace ConsoleApp
{
public static class Program
{
public static void Main()
{
Log.Logger.Information("Test" + " {My{caret}Property} prop", 1);
}
}
}
================================================
FILE: test/data/nuget.config
================================================
================================================
FILE: test/src/Analyzer/AnonymousTypeDestructureAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class AnonymousTypeDestructureAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "AnonymousTypeDestructure";
[Test] public void TestSerilogWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogWithComplexPropertyWithoutDestructure() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/ComplexObjectDestructureAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class ComplexObjectDestructureAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "ComplexTypeDestructure";
[Test] public void TestSerilogWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogForceStringWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogNumericWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogEnumerableWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogNullableWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogDictionaryWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogContextWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogContextNumericWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogContextExplicitDestructure() => DoNamedTest2();
[Test] public void TestSerilogCustomExceptionWithoutDestructure() => DoNamedTest2();
[Test] public void TestSerilogParentWithOverriddenToString() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/ContextualLoggerConstructorAnalyzerTests.cs
================================================
using JetBrains.ReSharper.TestFramework;
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
[TestNet60]
public class ContextualLoggerConstructorAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "ContextualLoggerConstructor";
[Test] public void TestMicrosoftCorrectContextType() => DoNamedTest2();
[Test] public void TestMicrosoftWrongContextType() => DoNamedTest2();
[Test] public void TestMicrosoftWrongContextTypeMultipleNamespaces() => DoNamedTest2();
[Test] public void TestMicrosoftWrongContextTypeMultipleParameters() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/ContextualLoggerSerilogFactoryAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class ContextualLoggerSerilogFactoryAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "ContextualLoggerSerilogFactory";
[Test] public void TestSerilogCorrectContextType() => DoNamedTest2();
[Test] public void TestSerilogWrongContextType() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/CorrectExceptionPassingAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class CorrectExceptionPassingAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "CorrectExceptionPassing";
[Test] public void TestSerilogCorrectExceptionPassing() => DoNamedTest2();
[Test] public void TestSerilogIncorrectExceptionPassing() => DoNamedTest2();
[Test] public void TestSerilogIncorrectExceptionPassingDynamicTemplate() => DoNamedTest2();
[Test] public void TestSerilogMultipleExceptionPassing() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/DuplicatePropertiesTemplateAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class DuplicatePropertiesTemplateAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "DuplicatePropertiesTemplate";
[Test] public void TestSerilogDuplicateNamedProperty() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/LogMessageIsSentenceAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class LogMessageIsSentenceAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "LogMessageIsSentence";
[Test] public void TestSerilogSentenceMessage() => DoNamedTest2();
[Test] public void TestSerilogNotSentenceMessage() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/MessageTemplateTests.cs
================================================
using JetBrains.Annotations;
using JetBrains.Application.Settings;
using JetBrains.ReSharper.Daemon.StringAnalysis;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.FeaturesTestFramework.Daemon;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.TestFramework;
using ReSharper.Structured.Logging.Highlighting;
using ReSharper.Structured.Logging.Tests.Constants;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
[TestPackages(
NugetPackages.SerilogNugetPackage,
NugetPackages.MicrosoftLoggingPackage,
NugetPackages.NlogLoggingPackage,
Inherits = true)]
public abstract class MessageTemplateAnalyzerTestBase : CSharpHighlightingTestBase
{
protected abstract string SubPath { get; }
protected override string RelativeTestDataPath => @"Analyzers\" + SubPath;
protected override void DoTestSolution([NotNull] params string[] fileSet)
{
ExecuteWithinSettingsTransaction(
settingsStore =>
{
RunGuarded(() => MutateSettings(settingsStore));
base.DoTestSolution(fileSet);
});
}
protected override bool HighlightingPredicate(
IHighlighting highlighting,
IPsiSourceFile sourceFile,
IContextBoundSettingsStore settingsStore)
{
return highlighting is TemplateFormatStringNonExistingArgumentWarning
|| highlighting is StringEscapeCharacterHighlighting
|| highlighting is DuplicateTemplatePropertyWarning
|| highlighting is AnonymousObjectDestructuringWarning
|| highlighting is ContextualLoggerWarning
|| highlighting is ExceptionPassedAsTemplateArgumentWarning
|| highlighting is ComplexObjectDestructuringWarning
|| highlighting is ComplexObjectDestructuringInContextWarning
|| highlighting is PositionalPropertyUsedWarning
|| highlighting is InconsistentLogPropertyNamingWarning
|| highlighting is InconsistentContextLogPropertyNamingWarning
|| highlighting is LogMessageIsSentenceWarning;
}
protected virtual void MutateSettings([NotNull] IContextBoundSettingsStore settingsStore)
{
}
}
}
================================================
FILE: test/src/Analyzer/PositionalPropertiesUsageAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class PositionalPropertiesUsageAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "PositionalPropertiesUsage";
[Test] public void TestSerilogPositionProperty() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/PropertiesElasticNamingAnalyzerTests.cs
================================================
using JetBrains.Application.Settings;
using NUnit.Framework;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
// ReSharper disable once TestFileNameWarning
public class PropertiesElasticNamingAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "PropertiesNamingAnalyzer";
[Test] public void TestSerilogInvalidElasticNamedProperty() => DoNamedTest2();
protected override void MutateSettings(IContextBoundSettingsStore settingsStore)
{
settingsStore.SetValue(settings => settings.PropertyNamingType, PropertyNamingType.ElasticNaming);
}
}
}
================================================
FILE: test/src/Analyzer/PropertiesIgnoredRegexNamingAnalyzerTests.cs
================================================
using JetBrains.Application.Settings;
using NUnit.Framework;
using ReSharper.Structured.Logging.Settings;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
// ReSharper disable once TestFileNameWarning
public class PropertiesIgnoredRegexNamingAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "PropertiesNamingAnalyzer";
[Test] public void TestSerilogIgnoredInvalidNamedProperty() => DoNamedTest2();
protected override void MutateSettings(IContextBoundSettingsStore settingsStore)
{
settingsStore.SetValue(settings => settings.IgnoredPropertiesRegex,"MY_.*");
}
}
}
================================================
FILE: test/src/Analyzer/PropertiesNamingAnalyzerDotNet6Tests.cs
================================================
using JetBrains.ReSharper.TestFramework;
using NUnit.Framework;
using ReSharper.Structured.Logging.Tests.Constants;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
[TestNet60]
[TestPackages(
NugetPackages.ZLoggerLoggingPackage,
Inherits = true)]
public class PropertiesNamingAnalyzerDotNet6Tests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "PropertiesNamingAnalyzerDotNet6";
[Test] public void TestZLoggerInvalidNamedProperty() => DoNamedTest2();
}
}
================================================
FILE: test/src/Analyzer/PropertiesNamingAnalyzerTests.cs
================================================
using NUnit.Framework;
namespace ReSharper.Structured.Logging.Tests.Analyzer
{
public class PropertiesNamingAnalyzerTests : MessageTemplateAnalyzerTestBase
{
protected override string SubPath => "PropertiesNamingAnalyzer";
[Test] public void TestSerilogInvalidNamedProperty() => DoNamedTest2();
[Test] public void TestSerilogValidNamedProperty() => DoNamedTest2();
[Test] public void TestSerilogValidDestructuredNamedProperty() => DoNamedTest2();
[Test] public void TestSerilogContextInvalidNamedProperty() => DoNamedTest2();
[Test] public void TestSerilogContextInterpolatedStringProperty() => DoNamedTest2();
[Test] public void TestSerilogInvalidNamedPropertyWithDot() => DoNamedTest2();
[Test] public void TestSerilogInvalidSyntax() => DoNamedTest2();
[Test] public void TestSerilogInvalidNamedPropertyWithSpace() => DoNamedTest2();
}
}
================================================
FILE: test/src/Constants/NugetPackages.cs
================================================
namespace ReSharper.Structured.Logging.Tests.Constants
{
internal static class NugetPackages
{
public const string SerilogNugetPackage = "Serilog/2.7.1";
public const string MicrosoftLoggingPackage = "Microsoft.Extensions.Logging/6.0.0";
public const string NlogLoggingPackage = "NLog/4.5.11";
public const string ZLoggerLoggingPackage = "ZLogger/1.7.0";
}
}
================================================
FILE: test/src/QuickFixes/AddDestructuringToMessageTemplatePropertyFixTests.cs
================================================
using NUnit.Framework;
using ReSharper.Structured.Logging.QuickFixes;
namespace ReSharper.Structured.Logging.Tests.QuickFixes
{
public class AddDestructuringToMessageTemplatePropertyFixTests : QuickFixTestBase
{
protected override string SubPath => "AddDestructuringFix";
[Test] public void TestSerilogEscapedString() => DoNamedTest2();
[Test] public void TestSerilogNewAnonymousObject() => DoNamedTest2();
[Test] public void TestSerilogNewComplexObject() => DoNamedTest2();
}
}
================================================
FILE: test/src/QuickFixes/QuickFixTestBase.cs
================================================
using JetBrains.ReSharper.Feature.Services.QuickFixes;
using JetBrains.ReSharper.FeaturesTestFramework.Intentions;
using JetBrains.ReSharper.TestFramework;
using NUnit.Framework;
using ReSharper.Structured.Logging.Tests.Constants;
namespace ReSharper.Structured.Logging.Tests.QuickFixes
{
[TestFixture]
[TestNetFramework46]
[TestPackages(
NugetPackages.SerilogNugetPackage,
NugetPackages.MicrosoftLoggingPackage,
NugetPackages.NlogLoggingPackage,
Inherits = true)]
// ReSharper disable once TestClassNameSuffixWarning
public abstract class QuickFixTestBase : CSharpQuickFixTestBase
where TQuickFix : IQuickFix
{
protected override string RelativeTestDataPath => @"QuickFixes\" + SubPath;
protected abstract string SubPath { get; }
}
}
================================================
FILE: test/src/QuickFixes/RemoveTrailingPeriodFixTests.cs
================================================
using NUnit.Framework;
using ReSharper.Structured.Logging.QuickFixes;
namespace ReSharper.Structured.Logging.Tests.QuickFixes
{
public class RemoveTrailingPeriodFixTests : QuickFixTestBase
{
protected override string SubPath => "RemoveTrailingPeriodFix";
[Test] public void TestSerilogTrailingPeriod() => DoNamedTest2();
}
}
================================================
FILE: test/src/QuickFixes/RenameContextLogPropertyFixTests.cs
================================================
using NUnit.Framework;
using ReSharper.Structured.Logging.QuickFixes;
namespace ReSharper.Structured.Logging.Tests.QuickFixes
{
public class RenameContextLogPropertyFixTests : QuickFixTestBase
{
protected override string SubPath => "RenameContextLogPropertyFix";
[Test] public void TestSerilogContextProperty() => DoNamedTest2();
}
}
================================================
FILE: test/src/QuickFixes/RenameLogPropertyFixTests.cs
================================================
using NUnit.Framework;
using ReSharper.Structured.Logging.QuickFixes;
namespace ReSharper.Structured.Logging.Tests.QuickFixes
{
public class RenameLogPropertyFixTests : QuickFixTestBase
{
protected override string SubPath => "RenameLogPropertyFix";
[Test] public void TestSerilogProperty() => DoNamedTest2();
[Test] public void TestSerilogDestructuredProperty() => DoNamedTest2();
[Test] public void TestSerilogPropertyConcatenated() => DoNamedTest2();
}
}
================================================
FILE: test/src/ReSharper.Structured.Logging.Rider.Tests.csproj
================================================
net472
false
true
ReSharper.Structured.Logging.Tests
RIDER
bin\$(MSBuildProjectName)\$(Configuration)\
================================================
FILE: test/src/ReSharper.Structured.Logging.Tests.csproj
================================================
net472
false
bin\$(MSBuildProjectName)\$(Configuration)\
================================================
FILE: test/src/TestEnvironment.cs
================================================
using JetBrains.Application.BuildScript.Application.Zones;
using JetBrains.ReSharper.TestFramework;
using JetBrains.TestFramework;
using JetBrains.TestFramework.Application.Zones;
using NUnit.Framework;
#if RIDER
using JetBrains.Rider.Backend.Env;
#endif
[assembly: RequiresThread(System.Threading.ApartmentState.STA)]
namespace ReSharper.Structured.Logging.Tests
{
[ZoneDefinition]
public interface IReSharperSerilog : ITestsEnvZone, IRequire
#if RIDER
, IRequire
#endif
{
}
[SetUpFixture]
public class TestEnvironment : ExtensionTestEnvironmentAssembly
{
}
}
================================================
FILE: test/src/app.config
================================================