Skip to content

Commit

Permalink
Merge pull request #111 from jonisavo/feature/UIC003
Browse files Browse the repository at this point in the history
Add analyzer rule for disallowing readonly fields with QueryAttribute or ProvideAttribute
  • Loading branch information
jonisavo authored Sep 26, 2023
2 parents 7956148 + a800f5d commit 583103a
Show file tree
Hide file tree
Showing 31 changed files with 387 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .release-it.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ plugins:
ignoreRecommendedBump: true

hooks:
after:bump: ["node ./Scripts/update_snapshot_versions.js ${version}", "node ./Scripts/update_package_version.js ${version}", "node ./Scripts/generate_unitypackage.js v${version}"]
after:bump: ["node ./Scripts/update_package_version.js ${version}", "node ./Scripts/update_snapshot_versions.js ${version}", "node ./Scripts/generate_unitypackage.js v${version}"]
Binary file not shown.
Binary file not shown.
Binary file modified Assets/UIComponents/Roslyn/UIComponents.Roslyn.Analyzers.dll
Binary file not shown.
Binary file modified Assets/UIComponents/Roslyn/UIComponents.Roslyn.Analyzers.pdb
Binary file not shown.
Binary file modified Assets/UIComponents/Roslyn/UIComponents.Roslyn.Common.dll
Binary file not shown.
Binary file modified Assets/UIComponents/Roslyn/UIComponents.Roslyn.Common.pdb
Binary file not shown.
Binary file modified Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.dll
Binary file not shown.
Binary file modified Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.pdb
Binary file not shown.
4 changes: 2 additions & 2 deletions Packages/manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dependencies": {
"com.unity.addressables": "1.18.19",
"com.unity.ide.rider": "3.0.24",
"com.unity.ide.visualstudio": "2.0.18",
"com.unity.ide.rider": "3.0.25",
"com.unity.ide.visualstudio": "2.0.21",
"com.unity.ide.vscode": "1.2.5",
"com.unity.test-framework": "1.1.33",
"com.unity.test-framework.performance": "3.0.0-pre.2",
Expand Down
4 changes: 2 additions & 2 deletions Packages/packages-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
"version": "3.0.24",
"version": "3.0.25",
"depth": 0,
"source": "registry",
"dependencies": {
Expand All @@ -31,7 +31,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.visualstudio": {
"version": "2.0.18",
"version": "2.0.21",
"depth": 0,
"source": "registry",
"dependencies": {
Expand Down
28 changes: 26 additions & 2 deletions Scripts/update_package_version.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// update_package_version.js
//
// This script updates the version in the package's package.json file,
// as well as all occurrences of it in the README.md file.
// as well as all occurrences of it in the README.md file and Roslyn projects.
//

const path = require('path');
Expand Down Expand Up @@ -50,8 +50,32 @@ function replaceVersionInFile(filePath) {
}

const readmePath = path.resolve(__dirname, '..', 'README.md');
const roslynSolutionPath = path.resolve(__dirname, '..', 'UIComponents.Roslyn', 'UIComponents.Roslyn.sln');

/**
* @param {string} startDir
* @returns {string[]}
*/
function findCsprojFiles(currentDir) {
let csprojFiles = [];
const files = fs.readdirSync(currentDir);

for (const file of files) {
const filePath = path.join(currentDir, file);
const isDirectory = fs.statSync(filePath).isDirectory();

if (isDirectory) {
csprojFiles = csprojFiles.concat(findCsprojFiles(filePath));
} else if (file.endsWith('.csproj')) {
csprojFiles.push(filePath);
}
}

return csprojFiles;
}

const pathsToReplace = [readmePath];
const roslynProjectPaths = findCsprojFiles(path.resolve(__dirname, '..', 'UIComponents.Roslyn'));
const pathsToReplace = [readmePath, roslynSolutionPath, ...roslynProjectPaths];

for (const path of pathsToReplace) {
replaceVersionInFile(path);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,7 @@
<data name="PartialFix_Title" xml:space="preserve">
<value>Make class partial</value>
</data>
<data name="InvalidReadonlyFix_Title" xml:space="preserve">
<value>Remove readonly keyword</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
diagnostic);
}

private async Task<Document> RemoveAttribute(Document document, AttributeSyntax attributeSyntax, CancellationToken cancellationToken)
private async Task<Document> RemoveAttribute(Document document, AttributeSyntax attributeSyntax, CancellationToken cancellationToken)
{
var attributeList = attributeSyntax.Parent as AttributeListSyntax;
var classDeclaration = attributeList.Parent;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;

namespace UIComponents.Roslyn.Analyzers
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(InvalidReadonlyCodeFixProvider)), Shared]
public sealed class InvalidReadonlyCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(InvalidReadonlyMemberAnalyzer.DiagnosticId); }
}

public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;

var fieldDeclaration = root.FindToken(diagnosticSpan.Start).Parent
.AncestorsAndSelf()
.OfType<FieldDeclarationSyntax>()
.First();

context.RegisterCodeFix(
CodeAction.Create(
title: CodeFixResources.InvalidReadonlyFix_Title,
createChangedDocument: cancellationToken => RemoveReadonly(context.Document, fieldDeclaration, cancellationToken),
equivalenceKey: nameof(CodeFixResources.InvalidReadonlyFix_Title)),
diagnostic);
}

private async Task<Document> RemoveReadonly(Document document, FieldDeclarationSyntax fieldDeclaration, CancellationToken cancellationToken)
{
var typeDeclaration = fieldDeclaration
.Ancestors()
.OfType<BaseTypeDeclarationSyntax>()
.First();

var editor = new SyntaxEditor(typeDeclaration, document.Project.Solution.Workspace);

var newModifiers = fieldDeclaration.Modifiers
.Where(modifier => modifier.Kind() != SyntaxKind.ReadOnlyKeyword);
var newFieldDeclaration = fieldDeclaration.WithModifiers(new SyntaxTokenList(newModifiers));
editor.ReplaceNode(fieldDeclaration, newFieldDeclaration);

var newDeclaration = editor.GetChangedRoot();

var root = await document.GetSyntaxRootAsync(cancellationToken);
var newRoot = root.ReplaceNode(typeDeclaration, newDeclaration);
var newDocument = document.WithSyntaxRoot(newRoot);

return newDocument;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<IsRoslynComponent>true</IsRoslynComponent>
<RootNamespace>UIComponents.Roslyn.Analyzers</RootNamespace>
<ReleaseVersion>1.0.0-beta.7</ReleaseVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<ReleaseVersion>1.0.0-beta.7</ReleaseVersion>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading.Tasks;
using VerifyCS = UIComponents.Roslyn.Analyzers.Test.CSharpCodeFixVerifier<
UIComponents.Roslyn.Analyzers.InvalidReadonlyMemberAnalyzer,
UIComponents.Roslyn.Analyzers.InvalidReadonlyCodeFixProvider>;

namespace UIComponents.Roslyn.Analyzers.Test
{
[TestClass]
public class InvalidReadonlyMemberAnalyzerTests
{
private const string UIComponentsDefinition = @"
namespace UIComponents
{
public class ProvideAttribute : Attribute
{
public ProvideAttribute() {}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class QueryAttribute : Attribute
{
public QueryAttribute(string id) {}
}
}
";

[TestMethod]
public async Task It_Reports_Readonly_Query_And_Provide_Members()
{
var test = $@"
using System;
{UIComponentsDefinition}
namespace Application
{{
public class OtherAttribute : Attribute {{}}
public class TestComponent
{{
{{|#0:[UIComponents.Provide]
public readonly string first;|}}
{{|#1:[Other, UIComponents.Query(""test"")]
[UIComponents.Query(""test2"")]
public readonly string second;|}}
}}
}}";

var fixtest = $@"
using System;
{UIComponentsDefinition}
namespace Application
{{
public class OtherAttribute : Attribute {{}}
public class TestComponent
{{
[UIComponents.Provide]
public string first;
[Other, UIComponents.Query(""test"")]
[UIComponents.Query(""test2"")]
public string second;
}}
}}";
var firstResult = VerifyCS.Diagnostic("UIC003")
.WithLocation(0)
.WithArguments("ProvideAttribute");
var secondResult = VerifyCS.Diagnostic("UIC003")
.WithLocation(1)
.WithArguments("QueryAttribute");
await VerifyCS.VerifyCodeFixAsync(test,
new DiagnosticResult[] { firstResult, secondResult },
fixtest
);
}

[TestMethod]
public async Task It_Does_Not_Report_Non_Readonly_Fields()
{
var test = $@"
using System;
{UIComponentsDefinition}
namespace Application
{{
public class OtherAttribute : Attribute {{}}
public class TestComponent
{{
[UIComponents.Provide]
public string first;
[Other, UIComponents.Query(""test"")]
public string second;
}}
}}";
await VerifyCS.VerifyAnalyzerAsync(test);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<ReleaseVersion>1.0.0-beta.7</ReleaseVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
; Shipped analyzer releases
; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

## Release 1.0.0-alpha.6

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2020.3.48f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2020.3.48f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2020.3.48f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2020.3.48f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2021.3.29f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2021.3.29f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2021.3.29f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2021.3.29f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2022.3.7f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2022.3.7f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2022.3.7f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2022.3.7f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2023.1.8f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2023.1.8f1 (StandaloneLinux64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2023.1.8f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 4 in UIComponents.Roslyn/UIComponents.Roslyn.Analyzers/UIComponents.Roslyn.Analyzers/AnalyzerReleases.Shipped.md

View workflow job for this annotation

GitHub Actions / Test on 2023.1.8f1 (StandaloneWindows64)

Analyzer release file 'AnalyzerReleases.Shipped.md' has a missing or invalid release header '## Release 1.0.0-alpha.6'. (https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

### New Rules

Rule ID | Category | Severity | Notes
---------|----------|----------|----------------------------------------
UIC001 | Core | Error | UIComponentPartialAnalyzer
UIC002 | Core | Warning | UIComponentInternalMethodAnalyzer
UIC101 | Layout | Warning | UIComponentIdenticalStylesheetAnalyzer
UIC102 | Layout | Error | EmptyUxmlNameAnalyzer
UIC103 | Layout | Error | EmptyUxmlTraitNameAnalyzer
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
UIC103 | Category | Error | EmptyUxmlTraitNameAnalyzer
Loading

0 comments on commit 583103a

Please sign in to comment.