I'm using C# 10 new feature File-scoped namespace declaration.
I have old code like this
namespace SampleCode
{
public class MyClass
{
}
}
I'm moving this code to
namespace SampleCode;
public class MyClass
{
}
But I have a bunch of warnings : IDE0160: Convert to block scoped namespace
How do I make sure people will have warnings only with old syntax ?
To control the code style in editorconfig use this line :
To enforce this style
namespace SampleCode
{
public class MyClass
{
}
}
Add this line in .editorconfig
# IDE0160: Convert to block-scoped namespace
csharp_style_namespace_declarations = block_scoped:warning
To enforce this style
namespace SampleCode;
public class MyClass
{
}
Add this line in .editorconfig
# IDE0160: Convert to file-scoped namespace
csharp_style_namespace_declarations = file_scoped:warning
Update 2022-01-27 (all scenarios setup)
JetBrains Rider does support the dotnet_diagnostic.IDE* syntax starting from version 2021.3.2.This simplifies the setup for all possible scenarios into this:
EditorConfig
csharp_style_namespace_declarations = file_scoped
dotnet_diagnostic.IDE0161.severity = error
CSProj
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
This will cover all the scenarios. Original answer below. Still worth to read it.
There are several different settings which you should control based on your desired state, used IDEs and workflow.
They are described in this article which I strongly recommend to read before you start building .editorconfig for your project.
Here is a summary for File-scoped, Block-scoped usings, respectively.
EditorConfig/CSproj setup for File-scoped usings
Visual Studio (error on violation)
EditorConfig
csharp_style_namespace_declarations = file_scoped
dotnet_diagnostic.IDE0161.severity = error
Note
Syntax option = rule:severity will be deprecated, sooner or later.
JetBrains Rider (error on violation)
EditorConfig
csharp_style_namespace_declarations = file_scoped:error
Note
Rider doesn't support dotnet_diagnostic.IDE* syntax.
CLI build e.g., CI/CD pipeline
EditorConfig
csharp_style_namespace_declarations = file_scoped
dotnet_diagnostic.IDE0161.severity = error
CSProj
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
Recommended setup
EditorConfig
csharp_style_namespace_declarations = file_scoped:error
dotnet_diagnostic.IDE0161.severity = error
CSProj
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
Note
Is the current .NET EditorConfig syntax a mess? Definitely.
EditorConfig/CSproj setup for Block-scoped usings
Visual Studio (error on violation)
EditorConfig
csharp_style_namespace_declarations = block_scoped
dotnet_diagnostic.IDE0160.severity = error
Note
Syntax option = rule:severity will be deprecated, sooner or later.
JetBrains Rider (error on violation)
EditorConfig
csharp_style_namespace_declarations = block_scoped:error
Note
Rider doesn't support dotnet_diagnostic.IDE* syntax.
CLI build e.g., CI/CD pipeline
EditorConfig
csharp_style_namespace_declarations = block_scoped
dotnet_diagnostic.IDE0160.severity = error
CSProj
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
Recommended setup
EditorConfig
csharp_style_namespace_declarations = block_scoped:error
dotnet_diagnostic.IDE0160.severity = error
CSProj
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
Related
I'm hoping to see CA1062, Validate Argument of public methods. But I'm not. It seems Code analysis is running because I see CA1822 for some items.
Here's an example of code I'm hoping would trigger CA1062.
public class FooController : BaseController<IFooProxy>
{
[HttpPost]
[Route("bar")]
public async Task<bool> Bar(string baz)
{
bool hasXyz = baz.Contains("xyz");
Console.WriteLine(hasXyz);
This is the csproj settings:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>preview</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>
It's actually just a desperate attempt to turn everything on... I find myself missing the previous method, with a property page in the project to select rule set.
StyleCop is long gone, if the above doesn't work somehow, is there any open source way to run the rules?
If you want to enable all rules in .net 5, you should use:
<AnalysisLevel>5.0</AnalysisLevel>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
BTW in .net 6, it's:
<AnalysisMode>All</AnalysisMode>
https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#analysismode
I enabled XML documentation generation for my project.
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
This generates a lot of warnings for my project because I don't compulsively add or update the XML doc comments for each and every type or member in my project. I turned the warnings back off with another tactically placed MSBuild instruction.
<PropertyGroup>
<NoWarn>$(NoWarn);1591</NoWarn> <!-- CS1591: Missing XML comment for publicly visible type or member -->
<NoWarn>$(NoWarn);1573</NoWarn> <!-- CS1573: Parameter '' has no matching param tag in the XML comment -->
</PropertyGroup>
Now the question becomes, how do I restore the warnings for the files where I do want to have clean documentation or a warning?
What I tried, didn't work.
#pragma warning restore CS1591,CS1573
public class Undocumented
{
public void AlsoUndocumented(bool undocumented = true) { }
}
I am currently building a tool which will support the development of an ASP.NET Core project. This tool uses the Roslyn APIs and other methods for verifying some development requirements (such as project-specific attributes being applied on API Controllers, enforcing naming conventions, and generating some source code for the JavaScript SPA which accesses an API written using the ASP.NET Core Web API template).
In order to do that, I am currently using hardcoded paths to generate code for the SPA app. But in the app's *.csproj file there is actually a "SpaRoot" property specifying where the SPA application is located inside the project:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>ClientApp\</SpaRoot>
...
</PropertyGroup>
...
</Project>
My question is: how can I read the "SpaRoot" property's value using the Roslyn APIs?
I have written a minimum code sample to create a Workspace, open the Solution, and retrieve the Project's reference, which resembles the following:
static async Task Main(string[] args)
{
string solutionFile = #"C:\Test\my-solution.sln";
using (var workspace = MSBuildWorkspace.Create())
{
var solution = await workspace.OpenSolutionAsync(solutionFile);
string projectName = "some-project";
var project = solution.Projects.Single(p => p.Name == projectName);
// How to extract the value of "SpaRoot" from the Project here?
}
I've tried searching on how to extract the "SpaRoot" property from the Project reference, and even went as far as debugging to see if I could spot a way myself. Unfortunately, I came up with no answers to that, and I'm still using hardcoded paths in my original code.
Is it even possible to retrieve the value of .csproj properties of a Project using the current Roslyn APIs?
This is more difficult that you would think :) The Roslyn apis only know what the compiler knows and the compiler is not going to be given anything regarding the SpaRoot property. We can use the MSBuild apis to figure this out though. specifically the Microsoft.Build.Evaluation.Project class.
Some assumptions I am making
You only want to examine .NET Core projects
You will have the .NET Core SDK installed on which ever system runs this tool
So first we want a project file that looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<!--NOTE: If the project you are analyzing is .NET Core then the commandline tool must be as well.
.NET Framework console apps cannot load .NET Core MSBuild assemblies which is required
for what we want to do.-->
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<!-- NOTE: We put ExcludeAssets="runtime" on all direct MSBuild references so that we pick up whatever
version is being used by the .NET SDK instead. This is accomplished with the Microsoft.Build.Locator
referenced further below. -->
<PackageReference Include="Microsoft.Build" Version="16.4.0" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.4.0" />
<!-- NOTE: A lot of MSBuild tasks that we are going to load in order to analyze a project file will implicitly
load build tasks that will require Newtonsoft.Json version 9. Since there is no way for us to ambiently
pick these dependencies up like with MSBuild assemblies we explicitly reference it here. -->
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>
</Project>
and a Program.cs file that looks like this:
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis.MSBuild;
// I use this so I don't get confused with the Roslyn Project type
using MSBuildProject = Microsoft.Build.Evaluation.Project;
namespace loadProject {
class Program {
static async Task Main(string[] args) {
MSBuildWorkspaceSetup();
// NOTE: we need to make sure we call MSBuildLocator.RegisterInstance
// before we ask the CLR to load any MSBuild types. Therefore we moved
// the code that uses MSBuild types to its own method (instead of being in
// Main) so the CLR is not forced to load them on startup.
await DoAnalysisAsync(args[0]);
}
private static async Task DoAnalysisAsync(string solutionPath) {
using var workspace = MSBuildWorkspace.Create();
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// We just select the first project as a demo
// you will want to use your own logic here
var project = solution.Projects.First();
// Now we use the MSBuild apis to load and evaluate our project file
using var xmlReader = XmlReader.Create(File.OpenRead(project.FilePath));
ProjectRootElement root = ProjectRootElement.Create(xmlReader, new ProjectCollection(), preserveFormatting: true);
MSBuildProject msbuildProject = new MSBuildProject(root);
// We can now ask any question about the properties or items in our project file
// and get the correct answer
string spaRootValue = msbuildProject.GetPropertyValue("SpaRoot");
}
private static void MSBuildWorkspaceSetup() {
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances.Length == 1
// If there is only one instance of MSBuild on this machine, set that as the one to use.
? visualStudioInstances[0]
// Handle selecting the version of MSBuild you want to use.
: SelectVisualStudioInstance(visualStudioInstances);
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
}
private static VisualStudioInstance SelectVisualStudioInstance(VisualStudioInstance[] visualStudioInstances) {
Console.WriteLine("Multiple installs of MSBuild detected please select one:");
for (int i = 0; i < visualStudioInstances.Length; i++) {
Console.WriteLine($"Instance {i + 1}");
Console.WriteLine($" Name: {visualStudioInstances[i].Name}");
Console.WriteLine($" Version: {visualStudioInstances[i].Version}");
Console.WriteLine($" MSBuild Path: {visualStudioInstances[i].MSBuildPath}");
}
while (true) {
var userResponse = Console.ReadLine();
if (int.TryParse(userResponse, out int instanceNumber) &&
instanceNumber > 0 &&
instanceNumber <= visualStudioInstances.Length) {
return visualStudioInstances[instanceNumber - 1];
}
Console.WriteLine("Input not accepted, try again.");
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress> {
public void Report(ProjectLoadProgress loadProgress) {
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null) {
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\\:ss\\.fffffff} {projectDisplay}");
}
}
}
}
I'm implementing a Roslyn analyzer and I want to take different action based on how some properties are set within the csproj.
Currently I'm accomplishing this by setting "AdditionalFiles" node in a props file imported with the analyzer. This points to the .csproj and then I manually xml-parse the project file looking for the properties I care about.
<ItemGroup>
<AdditionalFiles Include="$(ProjectPath)" />
</ItemGroup>
private void AnalyzeAdditionalFiles(CompilationStartAnalysisContext context)
{
ICompilationStartAnalysisContextWrapper wrappedContext = this.compilationStartAnalysisContextWrapperFactory.Create(context);
if (wrappedContext.GetAdditionalTexts()
.Any(addtionalFile => <xml parse and validate csproj>))
{
context.RegisterSyntaxNodeAction(this.AnalyzeSyntaxNode, PossibleSyntaxKinds);
}
}
I've been told there may be a first-class supported way to do one or both of these actions without requiring what feels like hacky versions of:
Find the path to the csproj
Fetch properties from the csproj
Is this possible? Ideally I'd be looking for the moral equivalent of
AnalysisContext.Project.Properties["MyCustomProp"]
It's now possible to retrieve some project properties, starting with VS 16.7 preview3. It's based around the new csproj tag CompilerVisibleProperty.
You can find more information on the source generator cookbook, but here's a quick example.
First, in your csproj file, you declare your property, and allow the analyzer to access it:
<!-- declare the property you want to access in your analyzer -->
<PropertyGroup>
<MyCustomProp>Value from csproj</MyCustomProp>
</PropertyGroup>
<!-- explicitly allow the analyzer to access that variable -->
<ItemGroup>
<CompilerVisibleProperty Include="MyCustomProp" />
</ItemGroup>
You can then access this variable from any analyzer context, via the AnalyzerConfigOptionsProvider.GlobalOptions. For example:
private static void CompilationStart(CompilationStartAnalysisContext context)
{
// retrieve the global analyzer options
var globalOptions = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions;
// retrieve the actual build property
// -> wanted property name prefixed with "build_property."
string myCustomProp = null;
if (!globalOptions.TryGetValue("build_property.MyCustomProp", out myCustomProp))
myCustomProp = "Default";
}
Nuget packages used for this example:
Microsoft.CodeAnalysis.Analyzers 3.3.3
Microsoft.CodeAnalysis.CSharp 4.0.1
There are plenty of resources online for bumping and reading version numbers using project.json. Given its deprecation and the re-introduction of .csproj, how do I go about setting the version number for a web project?
I've been able to read it with:
Microsoft.Extensions.PlatformAbstractions
.PlatformServices.Default.Application.ApplicationVersion
However, it always outputs 1.0.0.0, and I haven't been able to find where that version number is set.
Use the <VersionPrefix>5.4.3.2</VersionPrefix> property.
temp.csproj
...
<PropertyGroup>
<VersionPrefix>5.4.3.2</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyName>temp</AssemblyName>
<OutputType>Exe</OutputType>
</PropertyGroup>
...
Program.cs
public class Program
{
public static void Main()
{
var version = Microsoft
.Extensions
.PlatformAbstractions
.PlatformServices
.Default
.Application
.ApplicationVersion;
System.Console.WriteLine(version); // 5.4.3.2
}
}