Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="ConvertToInterpolatedString\ConvertToInterpolatedStringRefactoringTests.cs" />
<Compile Include="DocumentExtensions.cs" />
<Compile Include="ExpandExpressionBodiedMember\ExpandExpressionBodiedMemberRefactoringTests.cs" />
Expand Down
14 changes: 14 additions & 0 deletions Source/CSharpEssentials.Tests/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;

namespace CSharpEssentials.Tests
{
class Constants
{
public static readonly ImmutableList<MetadataReference> CompilerGeneratedAttributeReferences = ImmutableList.Create<MetadataReference>(
MetadataReference.CreateFromFile(typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location)
);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -402,5 +402,46 @@ public C(bool f)

NoDiagnostic(code, DiagnosticIds.UseGetterOnlyAutoProperty);
}

[Test]
public void NotAvailableInClassWithGeneratedCodeAttribute()
{
const string code = @"
using System;
[System.CodeDom.Compiler.GeneratedCode(""tool"", ""version"")]
class C
{
public bool MyProperty { get; private set; }
public C(bool f)
{
MyProperty = f;
}
}";

var document = TestHelpers.GetDocument(code, LanguageName, Constants.CompilerGeneratedAttributeReferences);

NoDiagnostic(document, DiagnosticIds.UseGetterOnlyAutoProperty);
}

[Test]
public void NotAvailableInPropertyWithGeneratedCodeAttribute()
{
const string code = @"
using System;
using System.CodeDom.Compiler;
class C
{
[GeneratedCode(""tool"", ""version"")]
public bool MyProperty { get; private set; }
public C(bool f)
{
MyProperty = f;
}
}";

var document = TestHelpers.GetDocument(code, LanguageName, Constants.CompilerGeneratedAttributeReferences);

NoDiagnostic(document, DiagnosticIds.UseGetterOnlyAutoProperty);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,44 @@ int Property

NoDiagnostic(code, DiagnosticIds.UseExpressionBodiedMember);
}

[Test]
public void NotAvailableInClassWithGeneratedCodeAttribute()
{
const string code = @"
using System;
[System.CodeDom.Compiler.GeneratedCode(""tool"", ""version"")]
class C
{
int Property
{
get { return 42; }
}
}";

var document = TestHelpers.GetDocument(code, LanguageName, Constants.CompilerGeneratedAttributeReferences);

NoDiagnostic(document, DiagnosticIds.UseExpressionBodiedMember);
}

[Test]
public void NotAvailableInMethodWithGeneratedCodeAttribute()
{
const string code = @"
using System;
using System.CodeDom.Compiler;
class C
{
[GeneratedCode(""tool"", ""version"")]
int Property
{
get { return 42; }
}
}";

var document = TestHelpers.GetDocument(code, LanguageName, Constants.CompilerGeneratedAttributeReferences);

NoDiagnostic(document, DiagnosticIds.UseExpressionBodiedMember);
}
}
}
41 changes: 41 additions & 0 deletions Source/CSharpEssentials.Tests/UseNameOf/UseNameOfAnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class UseNameOfAnalyzerTests : AnalyzerTestFixture
protected override string LanguageName => LanguageNames.CSharp;
protected override DiagnosticAnalyzer CreateAnalyzer() => new UseNameOfAnalyzer();


[Test]
public void NoDiagnosticWhenThereAreNoArguments()
{
Expand Down Expand Up @@ -182,5 +183,45 @@ void M(int x)

NoDiagnostic(code, DiagnosticIds.UseNameOf);
}

[Test]
public void NotAvailableInClassWithGeneratedCodeAttribute()
{
const string code = @"
using System;
[System.CodeDom.Compiler.GeneratedCode(""tool"", ""version"")]
class C
{
void M(int x)
{
throw new ArgumentNullException(""x"");
}
}";

var document = TestHelpers.GetDocument(code, LanguageName, Constants.CompilerGeneratedAttributeReferences);

NoDiagnostic(document, DiagnosticIds.UseNameOf);
}

[Test]
public void NotAvailableInMethodWithGeneratedCodeAttribute()
{
const string code = @"
using System;
using System.CodeDom.Compiler;
class C
{
[GeneratedCode(""tool"", ""version"")]
void M(int x)
{
throw new ArgumentNullException(""x"");
}
}";

var document = TestHelpers.GetDocument(code, LanguageName, Constants.CompilerGeneratedAttributeReferences);

NoDiagnostic(document, DiagnosticIds.UseNameOf);
}

}
}
54 changes: 47 additions & 7 deletions Source/CSharpEssentials/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace CSharpEssentials
{
Expand Down Expand Up @@ -211,19 +212,58 @@ private static bool BeginsWithAutoGeneratedComment(SyntaxTree tree, Cancellation
return false;
}

public static bool IsGeneratedCode(this SyntaxTree tree, CancellationToken cancellationToken)
public static bool IsGeneratedCode(this SyntaxNode node, SemanticModel model, CancellationToken cancellationToken)
{
if (IsGeneratedCodeFile(tree.FilePath))
{
return true;
return IsGeneratedCodeFile(node.SyntaxTree.FilePath)
|| BeginsWithAutoGeneratedComment(node.SyntaxTree, cancellationToken)
|| IsInGeneratedCodeAttributeContext(node, model, cancellationToken);
}

public static bool IsGeneratedCode(this SyntaxNodeAnalysisContext context)
=> IsGeneratedCode(context.Node, context.SemanticModel, context.CancellationToken);

private static bool IsInGeneratedCodeAttributeContext(SyntaxNode node, SemanticModel model, CancellationToken cancelationToken)
{
while(node != null)
{
var typeDecleration = node as BaseTypeDeclarationSyntax;
if (typeDecleration != null && ContainsGeneratedCodeAttribute(typeDecleration.AttributeLists, model, cancelationToken))
return true;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about fields initializers, properties, indexers, operators, and conversions?


var methodDecleration = node as BaseMethodDeclarationSyntax;
if (methodDecleration != null && ContainsGeneratedCodeAttribute(methodDecleration.AttributeLists, model, cancelationToken))
return true;

var propertyDecleration = node as BasePropertyDeclarationSyntax;
if (propertyDecleration != null && ContainsGeneratedCodeAttribute(propertyDecleration.AttributeLists, model, cancelationToken))
return true;

var fieldDecleration = node as BaseFieldDeclarationSyntax;
if (fieldDecleration != null && ContainsGeneratedCodeAttribute(fieldDecleration.AttributeLists, model, cancelationToken))
return true;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about structs?


node = node.Parent;
}
return false;
}

if (BeginsWithAutoGeneratedComment(tree, cancellationToken))

private static bool ContainsGeneratedCodeAttribute(SyntaxList<AttributeListSyntax> attributeLists, SemanticModel model, CancellationToken cancellationToken)
{
foreach (var attributeList in attributeLists)
{
return true;
}
foreach (var attribute in attributeList.Attributes)
{
var attributeType = model.GetTypeInfo(attribute, cancellationToken).Type;

if (attributeType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::System.CodeDom.Compiler.GeneratedCodeAttribute")
{
return true;
}
}
}
return false;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public override void Initialize(AnalysisContext analysisContext)
private static void OnType(SymbolAnalysisContext context)
{
var type = (INamedTypeSymbol)context.Symbol;
var candidates = GetAutoPropsWithPrivateSetters(type, context.CancellationToken);
var candidates = GetAutoPropsWithPrivateSetters(type, context);
if (candidates == null || candidates.Count == 0)
{
return;
Expand All @@ -38,7 +38,7 @@ private static void OnType(SymbolAnalysisContext context)
foreach (var reference in type.DeclaringSyntaxReferences)
{
SemanticModel model = null;

// Walk the tree looking for identifiers
foreach (var node in reference.GetSyntax(context.CancellationToken).DescendantNodes())
{
Expand Down Expand Up @@ -92,7 +92,7 @@ private static bool IsIdentifierWithinAnAssignmentButNotInAConstructor(
return updatedSymbol == identifierSymbol && !IsWithinConstructorOf(updatingExpression.Parent, identifierSymbol.ContainingType, identifierSymbol.IsStatic, semanticModel, cancellationToken);
}

private static HashSet<ISymbol> GetAutoPropsWithPrivateSetters(INamedTypeSymbol type, CancellationToken cancellationToken)
private static HashSet<ISymbol> GetAutoPropsWithPrivateSetters(INamedTypeSymbol type, SymbolAnalysisContext context)
{
HashSet<ISymbol> candidates = null;

Expand All @@ -109,11 +109,15 @@ private static HashSet<ISymbol> GetAutoPropsWithPrivateSetters(INamedTypeSymbol
var reference = setMethod.DeclaringSyntaxReferences.FirstOrDefault();
if (reference != null)
{
var declaration = reference.GetSyntax(cancellationToken) as AccessorDeclarationSyntax;
var declaration = reference.GetSyntax(context.CancellationToken) as AccessorDeclarationSyntax;
if (declaration != null && declaration.Body == null)
{
// An empty body indicates it's an auto-prop
(candidates ?? (candidates = new HashSet<ISymbol>())).Add(propertySymbol);
var model = context.Compilation.GetSemanticModel(declaration.SyntaxTree);
if (!declaration.IsGeneratedCode(model, context.CancellationToken))
{
// An empty body indicates it's an auto-prop
(candidates ?? (candidates = new HashSet<ISymbol>())).Add(propertySymbol);
}
}
}
}
Expand Down Expand Up @@ -202,16 +206,12 @@ private static Diagnostic CreateDiagnostic(ISymbol symbol, CancellationToken can
// The span should be on the setter, but the symbol is for the whole property declaration.
var property = (PropertyDeclarationSyntax)reference.GetSyntax(cancellationToken);

// If the property declaration is in generated code, we shouldn't create the diagnostic.
if (!property.SyntaxTree.IsGeneratedCode(cancellationToken))
var setter = property.AccessorList.Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.SetAccessorDeclaration));
if (setter != null)
{
var setter = property.AccessorList.Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.SetAccessorDeclaration));
if (setter != null)
{
return Diagnostic.Create(
DiagnosticDescriptors.UseGetterOnlyAutoProperty,
setter.GetLocation());
}
return Diagnostic.Create(
DiagnosticDescriptors.UseGetterOnlyAutoProperty,
setter.GetLocation());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public override void Initialize(AnalysisContext context)

private static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
{
if (context.Node.SyntaxTree.IsGeneratedCode(context.CancellationToken))
if (context.IsGeneratedCode())
{
return;
}
Expand All @@ -49,7 +49,7 @@ private static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)

private void HandleOperatorDeclaration(SyntaxNodeAnalysisContext context)
{
if (context.Node.SyntaxTree.IsGeneratedCode(context.CancellationToken))
if (context.IsGeneratedCode())
{
return;
}
Expand All @@ -70,7 +70,7 @@ private void HandleOperatorDeclaration(SyntaxNodeAnalysisContext context)

private void HandleConversionOperatorDeclaration(SyntaxNodeAnalysisContext context)
{
if (context.Node.SyntaxTree.IsGeneratedCode(context.CancellationToken))
if (context.IsGeneratedCode())
{
return;
}
Expand All @@ -91,7 +91,7 @@ private void HandleConversionOperatorDeclaration(SyntaxNodeAnalysisContext conte

private static void HandlePropertyDeclaration(SyntaxNodeAnalysisContext context)
{
if (context.Node.SyntaxTree.IsGeneratedCode(context.CancellationToken))
if (context.IsGeneratedCode())
{
return;
}
Expand All @@ -112,7 +112,7 @@ private static void HandlePropertyDeclaration(SyntaxNodeAnalysisContext context)

private void HandleIndexerDeclaration(SyntaxNodeAnalysisContext context)
{
if (context.Node.SyntaxTree.IsGeneratedCode(context.CancellationToken))
if (context.IsGeneratedCode())
{
return;
}
Expand Down
2 changes: 1 addition & 1 deletion Source/CSharpEssentials/UseNameOf/UseNameOfAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public override void Initialize(AnalysisContext context)

private static void AnalyzeArgument(SyntaxNodeAnalysisContext context)
{
if (context.Node.SyntaxTree.IsGeneratedCode(context.CancellationToken))
if (context.IsGeneratedCode())
{
return;
}
Expand Down