Skip to content

Commit e6b18d1

Browse files
committed
# Conflicts:
# version.json
1 parent 926b3e1 commit e6b18d1

File tree

77 files changed

+10703
-407
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+10703
-407
lines changed

Diff for: .config/dotnet-tools.json

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
"dotnet-verify"
1616
],
1717
"rollForward": false
18+
},
19+
"squigglecop.tool": {
20+
"version": "1.0.13",
21+
"commands": [
22+
"dotnet-squigglecop"
23+
],
24+
"rollForward": false
1825
}
1926
}
2027
}

Diff for: .github/workflows/main.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
build:
3333
strategy:
3434
matrix:
35-
os: [windows-2022, ubuntu-22.04]
35+
os: [windows-latest, ubuntu-latest]
3636

3737
runs-on: ${{ matrix.os }}
3838

@@ -92,6 +92,13 @@ jobs:
9292
path: |
9393
**/*.received.*
9494
95+
- name: Upload SARIF files
96+
uses: actions/upload-artifact@v4
97+
if: success() || failure()
98+
with:
99+
name: SARIF files (${{ matrix.os }})
100+
path: ./artifacts/obj/**/*.sarif
101+
95102
- name: Upload Test Report
96103
uses: actions/upload-artifact@v4
97104
if: success() || failure()

Diff for: .github/workflows/release.yml

+6
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@ on:
55
release:
66
types:
77
- published # Run the workflow when a new GitHub release is published
8+
- edited
9+
- prereleased
10+
- released
811

912
permissions:
1013
security-events: write # required for CodeQL
1114
packages: read
1215
contents: read
1316
actions: read
1417

18+
env:
19+
PublicRelease: true # We want to generate stable package versions without the git hash in this action
20+
1521
jobs:
1622
build:
1723
uses: ./.github/workflows/main.yml

Diff for: CONTRIBUTING.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,14 @@ We welcome contributions. If you want to contribute to existing issues, check th
44
[help wanted](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/labels/help%20wanted) or
55
[good first issue](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/labels/good%20first%20issue) items in the backlog.
66

7-
If you have new ideas or want to complain about bugs, feel free to [create a new issue](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/issues/new).
7+
If you have new ideas or want to complain about bugs, feel free to [create a new issue](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/issues/new).
8+
9+
## Updating SquiggleCop baselines
10+
11+
To update SquiggleCop baselines, run this command and review and commit the results:
12+
13+
```powershell
14+
dotnet clean && dotnet build /p:PedanticMode=true /p:SquiggleCop_AutoBaseline=true
15+
```
16+
17+
`$(PedanticMode)` turns on the CI configuration (e.g. `TreatWarningsAsErrors`) and `$(SquiggleCop_AutoBaseline)`

Diff for: build/perf/baseline.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"release": "1.0.0",
3-
"label": "v.1.0.0",
4-
"sha": "e76647befbd50a2af11a4b888287542738b6e934"
2+
"release": "0.1.0",
3+
"label": "v0.1.0",
4+
"sha": "c0c5d965e88f59c3acbd867a74b55eed052df5b1"
55
}

Diff for: build/scripts/Set-SquiggleCopBaseline.ps1

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Get-ChildItem *.csproj -recurse -File | ForEach-Object{
2+
dotnet clean $_.FullName && dotnet build $_.FullName /p:PedanticMode=true /p:SquiggleCop_AutoBaseline=true
3+
}

Diff for: build/targets/codeanalysis/CodeAnalysis.props

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
<PrivateAssets>all</PrivateAssets>
3333
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3434
</PackageReference>
35+
<PackageReference Include="SquiggleCop.Tasks">
36+
<PrivateAssets>all</PrivateAssets>
37+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
38+
</PackageReference>
3539
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers">
3640
<PrivateAssets>all</PrivateAssets>
3741
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

Diff for: build/targets/codeanalysis/CodeAnalysis.targets

+22
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,24 @@
11
<Project>
2+
<PropertyGroup Label="Computed properties">
3+
<PedanticMode Condition=" '$(PedanticMode)' == '' ">$([MSBuild]::ValueOrDefault('$(ContinuousIntegrationBuild)', 'false'))</PedanticMode>
4+
<TreatWarningsAsErrors>$(PedanticMode)</TreatWarningsAsErrors>
5+
<MSBuildTreatWarningsAsErrors>$(PedanticMode)</MSBuildTreatWarningsAsErrors>
6+
<SquiggleCop_Enabled>$(PedanticMode)</SquiggleCop_Enabled>
7+
</PropertyGroup>
8+
9+
<Target Name="SetErrorLog" BeforeTargets="CoreCompile">
10+
<!--
11+
ErrorLog is needed for SquiggleCop.
12+
13+
The value is set in a Target and not directly as a property because `$(IntermediateOutputPath)` and `$(OutputPath)`
14+
are calculated properties and thus shouldn't be relied on during the initial property evaluation phase.
15+
See https://github.com/dotnet/sdk/issues/41852.
16+
17+
We use `$(IntermediateOutputPath)` to ensure the file ends up in the `obj/` folder and not with sources to clearly
18+
delineate inputs and outputs.
19+
-->
20+
<PropertyGroup Condition=" '$(ErrorLog)' == '' ">
21+
<ErrorLog>$(IntermediateOutputPath)/$(MSBuildProjectName).sarif,version=2.1</ErrorLog>
22+
</PropertyGroup>
23+
</Target>
224
</Project>

Diff for: build/targets/codeanalysis/Packages.props

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
55
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.4" />
66
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
7-
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.30.0.95878" />
7+
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.30.0.95878" />
8+
<PackageVersion Include="SquiggleCop.Tasks" Version="1.0.8" />
89
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.10.48" />
910
<PackageVersion Include="ExhaustiveMatching.Analyzer" Version="0.5.0" />
1011
</ItemGroup>

Diff for: build/targets/compiler/Compiler.props

-7
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@
55
<Nullable>enable</Nullable>
66
</PropertyGroup>
77

8-
<ItemGroup>
9-
<PackageReference Include="PolySharp">
10-
<PrivateAssets>all</PrivateAssets>
11-
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
12-
</PackageReference>
13-
</ItemGroup>
14-
158
<ItemGroup>
169
<AssemblyAttribute Include="System.CLSCompliantAttribute">
1710
<_Parameter1>false</_Parameter1>

Diff for: build/targets/compiler/Packages.props

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<Project>
2-
<ItemGroup>
3-
<PackageVersion Include="PolySharp" Version="1.14.1" />
4-
</ItemGroup>
2+
<ItemGroup>
3+
</ItemGroup>
54
</Project>

Diff for: docs/DOCUMENTING-ANALYZERS.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ private static readonly DiagnosticDescriptor Rule = new(
4040
category: "Maintainability",
4141
defaultSeverity: DiagnosticSeverity.Info,
4242
isEnabledByDefault: true,
43-
helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/{Id}.md");
43+
helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/rules/{Id}.md");
4444
```
4545

4646
The documentation for the rule is placed in to `docs/rules/ECS0002.md`.

Diff for: docs/RULE-REFERENCE-TEMPLATE.md

+17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@ This rule is described in detail in [Effective C#: 50 Specific Ways to Improve y
1010

1111
## When to suppress warnings
1212

13+
### Suppress a warning
14+
15+
If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.
16+
17+
```csharp
18+
#pragma warning disable RULEID
19+
// The code that's violating the rule
20+
#pragma warning restore RULEID
21+
```
22+
23+
To disable the rule for a file, folder, or project, set its severity to none in the [configuration file](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files).
24+
25+
```ini
26+
[*.cs]
27+
dotnet_diagnostic.RULEID.severity = none
28+
```
29+
1330
## Example of a violation
1431

1532
### Description

Diff for: docs/rules/ECS0002.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PRC001: Prefer readonly over const
1+
# ECS0002: Prefer readonly over const
22

33
## Cause
44
Using `const` fields for constants that do not need to be available at compile time.

Diff for: docs/rules/ECS0004.md

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ECS0004: Replace string.Format with interpolated string
2+
3+
This rule is described in detail in [Effective C#: 50 Specific Ways to Improve your C#](https://www.oreilly.com/library/view/effective-c-50/9780134579290/).
4+
5+
## Cause
6+
7+
Using `string.Format` for formatting strings instead of using interpolated strings.
8+
9+
## Rule description
10+
11+
This rule identifies the usage of `string.Format` method and suggests replacing it with C# interpolated strings. Interpolated strings enhance code readability and reduce the likelihood of runtime errors associated with mismatched format arguments.
12+
13+
## How to fix violations
14+
15+
Replace the usage of `string.Format` with an interpolated string. Interpolated strings are more readable and less error-prone compared to `string.Format`.
16+
17+
## When to suppress warnings
18+
19+
Suppress this warning if you have a specific reason to use `string.Format`, such as when dynamically constructing the format string at runtime or when maintaining compatibility with older code bases that rely on `string.Format`.
20+
21+
## Example of a violation
22+
23+
### Description
24+
25+
Using `string.Format` to format a string.
26+
27+
### Code
28+
29+
```csharp
30+
class Program
31+
{
32+
void Main()
33+
{
34+
var str = string.Format("Hello, {0}!", "world");
35+
}
36+
}
37+
38+
## Example of how to fix
39+
40+
### Description
41+
42+
Replacing `string.Format` with an interpolated string.
43+
44+
### Code
45+
46+
```csharp
47+
class Program
48+
{
49+
void Main()
50+
{
51+
var world = "world";
52+
var str = $"Hello, {world}!";
53+
}
54+
}
55+
```
56+
57+
## Related rules
58+
59+
[ECS0009: Minimize boxing and unboxing](./ECS0009.md)

Diff for: docs/rules/ECS0005.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# ECS0005: Prefer FormattableString for culture-specific strings
2+
3+
This rule is discussed in detail in [Effective C#: 50 Specific Ways to Improve your C#](https://www.oreilly.com/library/view/effective-c-50/9780134579290/). Guidance about the feature can be found on the [.NET Blog - String Interpolation in C# 10 and .NET 6](https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/).
4+
5+
## Cause
6+
7+
The rule is triggered when a `string` is used for an interpolated string that could benefit from culture-specific formatting.
8+
9+
## Rule description
10+
11+
There are still valid use cases for `string.Format` and `StringBuilder.AppendFormat` where the composite format string isn't known at compile time (e.g., localized resources). However, if the string would otherwise be hardcoded into the C#, interpolated are preferred (from a performance perspective). Using `FormattableString` or `string.Create` instead of `string` for interpolated strings ensures that culture-specific formatting is correctly applied, preventing issues where the default culture may lead to incorrect string representations.
12+
13+
## How to fix violations
14+
15+
Replace the `string` with `FormattableString` where culture-specific formatting is required. For example, use `FormattableString.Invariant(...)` or `FormattableString.ToString(IFormatProvider)` as needed.
16+
17+
## When to suppress warnings
18+
19+
Suppress warnings if the default culture is explicitly intended, or the string does not require culture-specific formatting.
20+
21+
### Suppress a warning
22+
23+
If you want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.
24+
25+
```csharp
26+
#pragma warning disable ECS0005
27+
// The code that's violating the rule
28+
#pragma warning restore ECS0005
29+
```
30+
31+
To disable the rule for a file, folder, or project, set its severity to none in the [configuration file](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files).
32+
33+
```ini
34+
[*.cs]
35+
dotnet_diagnostic.ECS0005.severity = none
36+
```
37+
38+
## Example of a violation
39+
40+
### Description
41+
42+
Using `string` for an interpolated string that requires culture-specific formatting.
43+
44+
### Code
45+
46+
```csharp
47+
public string GetMessage()
48+
{
49+
double value = 299792.458;
50+
return $"The speed of light is {value:N3} km/s."; // ECS0005 triggers here
51+
}
52+
```
53+
54+
## Example of how to fix
55+
56+
### Description
57+
58+
Replace `string` with `FormattableString` to handle culture-specific formatting.
59+
60+
### Code
61+
62+
```csharp
63+
public string GetMessage()
64+
{
65+
double value = 299792.458;
66+
return FormattableString.Invariant($"The speed of light is {value:N3} km/s.");
67+
}
68+
```
69+
70+
## Related rules
71+
72+
[ECS0004: Replace string.Format with interpolated string](./ECS0004.md)

0 commit comments

Comments
 (0)