Skip to content
Merged
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 @@ -125,6 +125,18 @@ private static void ComputeDeclarations(
return;
}

case SyntaxKind.ArrowExpressionClause:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's somewhat strange that the new shorthand syntax for property declarations has "Expression" in its name instead of "Declaration", but that's obviously not an issue for this PR. (Confused by the name, at first I thought this might be related to pointer dereferencing in unsafe code.)

I guess this is technically part of the expression that is the property's body. It still looks strange.

{
// Arrow expression clause declares getter symbol for properties and indexers.
var parentProperty = node.Parent as BasePropertyDeclarationSyntax;
if (parentProperty != null)
{
builder.Add(GetExpressionBodyDeclarationInfo(parentProperty, (ArrowExpressionClauseSyntax)node, model, getSymbol, cancellationToken));
}

return;
}

case SyntaxKind.PropertyDeclaration:
{
var t = (PropertyDeclarationSyntax)node;
Expand All @@ -133,7 +145,12 @@ private static void ComputeDeclarations(
foreach (var decl in t.AccessorList.Accessors) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
}

builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken, t.Initializer, t.ExpressionBody));
if (t.ExpressionBody != null)
{
ComputeDeclarations(model, t.ExpressionBody, shouldSkip, getSymbol, builder, levelsToCompute, cancellationToken);
}

builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken, t.Initializer));
return;
}

Expand All @@ -148,12 +165,13 @@ private static void ComputeDeclarations(
}
}

var codeBlocks = t.ParameterList != null ? t.ParameterList.Parameters.Select(p => p.Default) : SpecializedCollections.EmptyEnumerable<SyntaxNode>();
if (t.ExpressionBody != null)
{
codeBlocks = codeBlocks.Concat(t.ExpressionBody);
ComputeDeclarations(model, t.ExpressionBody, shouldSkip, getSymbol, builder, levelsToCompute, cancellationToken);
}

var codeBlocks = t.ParameterList != null ? t.ParameterList.Parameters.Select(p => p.Default) : SpecializedCollections.EmptyEnumerable<SyntaxNode>();

builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken));
return;
}
Expand Down Expand Up @@ -206,6 +224,22 @@ private static void ComputeDeclarations(
}
}

private static DeclarationInfo GetExpressionBodyDeclarationInfo(
BasePropertyDeclarationSyntax declarationWithExpressionBody,
ArrowExpressionClauseSyntax expressionBody,
SemanticModel model,
bool getSymbol,
CancellationToken cancellationToken)
{
// TODO: use 'model.GetDeclaredSymbol(expressionBody)' when compiler is fixed to return the getter symbol for it.
var declaredAccessor = getSymbol ? (model.GetDeclaredSymbol(declarationWithExpressionBody, cancellationToken) as IPropertySymbol)?.GetMethod : null;

return new DeclarationInfo(
declaredNode: expressionBody,
executableCodeBlocks: ImmutableArray.Create<SyntaxNode>(expressionBody),
declaredSymbol: declaredAccessor);
}

/// <summary>
/// Gets the expression-body syntax from an expression-bodied member. The
/// given syntax must be for a member which could contain an expression-body.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,8 +615,8 @@ public override void Initialize(AnalysisContext context)
SymbolKind.Namespace, SymbolKind.NamedType, SymbolKind.Event, SymbolKind.Field, SymbolKind.Method, SymbolKind.Property);
}
}
[Fact]

[Fact]
private void TestDisabledAnalyzers()
{
var fullyDisabledAnalyzer = new FullyDisabledAnalyzer();
Expand All @@ -635,79 +635,7 @@ private void TestDisabledAnalyzers()
Assert.True(partiallyDisabledAnalyzer.IsDiagnosticAnalyzerSuppressed(options));
}

private class CodeBlockAnalyzer : DiagnosticAnalyzer
{
public static DiagnosticDescriptor Desciptor1 = new TriggerDiagnosticDescriptor("CodeBlockDiagnostic");
public static DiagnosticDescriptor Desciptor2 = new TriggerDiagnosticDescriptor("EqualsValueDiagnostic");
public static DiagnosticDescriptor Desciptor3 = new TriggerDiagnosticDescriptor("ConstructorInitializerDiagnostic");
public static DiagnosticDescriptor Desciptor4 = new TriggerDiagnosticDescriptor("PropertyExpressionBodyDiagnostic");
public static DiagnosticDescriptor Desciptor5 = new TriggerDiagnosticDescriptor("IndexerExpressionBodyDiagnostic");
public static DiagnosticDescriptor Desciptor6 = new TriggerDiagnosticDescriptor("MethodExpressionBodyDiagnostic");

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(Desciptor1, Desciptor2, Desciptor3, Desciptor4, Desciptor5, Desciptor6);
}
}

public override void Initialize(AnalysisContext context)
{
context.RegisterCodeBlockStartAction<SyntaxKind>(new NodeAnalyzer().Initialize);
context.RegisterCodeBlockEndAction(OnCodeBlockEnded);
}

public static void OnCodeBlockEnded(CodeBlockEndAnalysisContext context)
{
context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Desciptor1, Location.None));
}

protected class NodeAnalyzer
{
public void Initialize(CodeBlockStartAnalysisContext<CSharp.SyntaxKind> analysisContext)
{
analysisContext.RegisterSyntaxNodeAction(
(context) =>
{
context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Desciptor2, Location.None));
},
CSharp.SyntaxKind.EqualsValueClause);

analysisContext.RegisterSyntaxNodeAction(
(context) =>
{
context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Desciptor3, Location.None));
},
CSharp.SyntaxKind.BaseConstructorInitializer);

analysisContext.RegisterSyntaxNodeAction(
(context) =>
{
DiagnosticDescriptor descriptor;
switch (context.Node.Parent.Kind())
{
case SyntaxKind.PropertyDeclaration:
descriptor = Desciptor4;
break;

case SyntaxKind.IndexerDeclaration:
descriptor = Desciptor5;
break;

default:
descriptor = Desciptor6;
break;
}

context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(descriptor, Location.None));
},
CSharp.SyntaxKind.ArrowExpressionClause);
}
}
}
[Fact, WorkItem(1008059)]

private void TestCodeBlockAnalyzersForNoExecutableCode()
{
string noExecutableCodeSource = @"
Expand All @@ -717,14 +645,14 @@ public abstract class C
public int field;
public abstract int Method();
}";
var analyzers = new DiagnosticAnalyzer[] { new CodeBlockAnalyzer() };
var analyzers = new DiagnosticAnalyzer[] { new CodeBlockOrSyntaxNodeAnalyzer(isCodeBlockAnalyzer: true) };

CreateCompilationWithMscorlib45(noExecutableCodeSource)
.VerifyDiagnostics()
.VerifyAnalyzerDiagnostics(analyzers);
}
[Fact, WorkItem(1008059)]

[Fact, WorkItem(1008059)]
private void TestCodeBlockAnalyzersForBaseConstructorInitializer()
{
string baseCtorSource = @"
Expand All @@ -737,7 +665,7 @@ public class C : B
{
public C() : base(x: 10) {}
}";
var analyzers = new DiagnosticAnalyzer[] { new CodeBlockAnalyzer() };
var analyzers = new DiagnosticAnalyzer[] { new CodeBlockOrSyntaxNodeAnalyzer(isCodeBlockAnalyzer: true)};

CreateCompilationWithMscorlib45(baseCtorSource)
.VerifyDiagnostics()
Expand All @@ -746,8 +674,8 @@ public C() : base(x: 10) {}
Diagnostic("CodeBlockDiagnostic"),
Diagnostic("CodeBlockDiagnostic"));
}
[Fact, WorkItem(1067286)]

[Fact, WorkItem(1067286)]
private void TestCodeBlockAnalyzersForExpressionBody()
{
string source = @"
Expand All @@ -757,7 +685,7 @@ public class B
public int Method() => 0;
public int this[int i] => 0;
}";
var analyzers = new DiagnosticAnalyzer[] { new CodeBlockAnalyzer() };
var analyzers = new DiagnosticAnalyzer[] { new CodeBlockOrSyntaxNodeAnalyzer(isCodeBlockAnalyzer: true) };

CreateCompilationWithMscorlib45(source)
.VerifyDiagnostics()
Expand All @@ -770,6 +698,46 @@ public class B
Diagnostic("MethodExpressionBodyDiagnostic"));
}

[Fact, WorkItem(592)]
private void TestSyntaxNodeAnalyzersForExpressionBody()
{
string source = @"
public class B
{
public int Property => 0;
public int Method() => 0;
public int this[int i] => 0;
}";
var analyzers = new DiagnosticAnalyzer[] { new CodeBlockOrSyntaxNodeAnalyzer(isCodeBlockAnalyzer: false) };

CreateCompilationWithMscorlib45(source)
.VerifyDiagnostics()
.VerifyAnalyzerDiagnostics(analyzers, null, null,
Diagnostic("PropertyExpressionBodyDiagnostic"),
Diagnostic("IndexerExpressionBodyDiagnostic"),
Diagnostic("MethodExpressionBodyDiagnostic"));
}

[Fact, WorkItem(592)]
private void TestMethodSymbolAnalyzersForExpressionBody()
{
string source = @"
public class B
{
public int Property => 0;
public int Method() => 0;
public int this[int i] => 0;
}";
var analyzers = new DiagnosticAnalyzer[] { new MethodSymbolAnalyzer() };

CreateCompilationWithMscorlib45(source)
.VerifyDiagnostics()
.VerifyAnalyzerDiagnostics(analyzers, null, null,
Diagnostic("MethodSymbolDiagnostic", "0").WithArguments("B.Property.get").WithLocation(4, 28),
Diagnostic("MethodSymbolDiagnostic", "Method").WithArguments("B.Method()").WithLocation(5, 16),
Diagnostic("MethodSymbolDiagnostic", "0").WithArguments("B.this[int].get").WithLocation(6, 31));
}

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class FieldDeclarationAnalyzer : DiagnosticAnalyzer
{
Expand Down Expand Up @@ -868,5 +836,107 @@ private void TestDescriptorForConfigurableCompilerDiagnostics()
}
}
}

public class CodeBlockOrSyntaxNodeAnalyzer : DiagnosticAnalyzer
{
private readonly bool _isCodeBlockAnalyzer;

public static DiagnosticDescriptor Desciptor1 = new TriggerDiagnosticDescriptor("CodeBlockDiagnostic");
public static DiagnosticDescriptor Desciptor2 = new TriggerDiagnosticDescriptor("EqualsValueDiagnostic");
public static DiagnosticDescriptor Desciptor3 = new TriggerDiagnosticDescriptor("ConstructorInitializerDiagnostic");
public static DiagnosticDescriptor Desciptor4 = new TriggerDiagnosticDescriptor("PropertyExpressionBodyDiagnostic");
public static DiagnosticDescriptor Desciptor5 = new TriggerDiagnosticDescriptor("IndexerExpressionBodyDiagnostic");
public static DiagnosticDescriptor Desciptor6 = new TriggerDiagnosticDescriptor("MethodExpressionBodyDiagnostic");

public CodeBlockOrSyntaxNodeAnalyzer(bool isCodeBlockAnalyzer)
{
_isCodeBlockAnalyzer = isCodeBlockAnalyzer;
}

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get { return ImmutableArray.Create(Desciptor1, Desciptor2, Desciptor3, Desciptor4, Desciptor5, Desciptor6); }
}

public override void Initialize(AnalysisContext context)
{
if (_isCodeBlockAnalyzer)
{
context.RegisterCodeBlockStartAction<SyntaxKind>(OnCodeBlockStarted);
context.RegisterCodeBlockEndAction(OnCodeBlockEnded);
}
else
{
Action<Action<SyntaxNodeAnalysisContext>, ImmutableArray<SyntaxKind>> registerMethod =
(action, Kinds) => context.RegisterSyntaxNodeAction(action, Kinds);
var analyzer = new NodeAnalyzer();
analyzer.Initialize(registerMethod);
}
}

public static void OnCodeBlockEnded(CodeBlockEndAnalysisContext context)
{
context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Desciptor1, Location.None));
}

public static void OnCodeBlockStarted(CodeBlockStartAnalysisContext<SyntaxKind> context)
{
Action<Action<SyntaxNodeAnalysisContext>, ImmutableArray<SyntaxKind>> registerMethod =
(action, Kinds) => context.RegisterSyntaxNodeAction(action, Kinds);
var analyzer = new NodeAnalyzer();
analyzer.Initialize(registerMethod);
}

protected class NodeAnalyzer
{
public void Initialize(Action<Action<SyntaxNodeAnalysisContext>, ImmutableArray<SyntaxKind>> registerSyntaxNodeAction)
{
registerSyntaxNodeAction(context => { context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Desciptor2, Location.None)); },
ImmutableArray.Create(SyntaxKind.EqualsValueClause));

registerSyntaxNodeAction(context => { context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Desciptor3, Location.None)); },
ImmutableArray.Create(SyntaxKind.BaseConstructorInitializer));

registerSyntaxNodeAction(context =>
{
var descriptor = default(DiagnosticDescriptor);
switch (CSharpExtensions.Kind(context.Node.Parent))
{
case SyntaxKind.PropertyDeclaration:
descriptor = Desciptor4;
break;
case SyntaxKind.IndexerDeclaration:
descriptor = Desciptor5;
break;
default:
descriptor = Desciptor6;
break;
}

context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(descriptor, Location.None));

}, ImmutableArray.Create(SyntaxKind.ArrowExpressionClause));
}
}
}

public class MethodSymbolAnalyzer : DiagnosticAnalyzer
{
public static DiagnosticDescriptor Desciptor1 = new DiagnosticDescriptor("MethodSymbolDiagnostic", "MethodSymbolDiagnostic", "{0}", "MethodSymbolDiagnostic", DiagnosticSeverity.Warning, isEnabledByDefault: true);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get { return ImmutableArray.Create(Desciptor1); }
}

public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(ctxt =>
{
var method = ((IMethodSymbol)ctxt.Symbol);
ctxt.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Desciptor1, method.Locations[0], method.ToDisplayString()));
}, SymbolKind.Method);
}
}
}
}
Loading