diff options
Diffstat (limited to 'main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/SimplifyTypeNames/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs')
-rw-r--r-- | main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/SimplifyTypeNames/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/SimplifyTypeNames/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/SimplifyTypeNames/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs new file mode 100644 index 0000000000..8a8de8f79d --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/SimplifyTypeNames/CSharpSimplifyTypeNamesDiagnosticAnalyzer.cs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using ICSharpCode.NRefactory6.CSharp; + +namespace MonoDevelop.CSharp.Diagnostics.SimplifyTypeNames +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class CSharpSimplifyTypeNamesDiagnosticAnalyzer : SimplifyTypeNamesDiagnosticAnalyzerBase<SyntaxKind> + { + private static readonly ImmutableArray<SyntaxKind> s_kindsOfInterest = ImmutableArray.Create(SyntaxKind.QualifiedName, + SyntaxKind.AliasQualifiedName, + SyntaxKind.GenericName, + SyntaxKind.IdentifierName, + SyntaxKind.SimpleMemberAccessExpression, + SyntaxKind.QualifiedCref); + + public override void Initialize(AnalysisContext analysisContext) + { + analysisContext.RegisterSyntaxNodeAction(AnalyzeNode, s_kindsOfInterest.ToArray()); + } + + protected override void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + if (context.IsFromGeneratedCode ()) + return; + if (context.Node.Ancestors(ascendOutOfTrivia: false).Any(n => s_kindsOfInterest.Contains(n.Kind()))) + { + // Already simplified an ancestor of this node. + return; + } + + Diagnostic diagnostic; + Func<SyntaxNode, bool> descendIntoChildren = n => + { + if (!IsRegularCandidate(n) || + !TrySimplifyTypeNameExpression(context.SemanticModel, n, context.Options, out diagnostic, context.CancellationToken)) + { + return true; + } + context.ReportDiagnostic(diagnostic); + return false; + }; + + // find regular node first - search from top to down. once found one, don't get into its children + foreach (var candidate in context.Node.DescendantNodesAndSelf(descendIntoChildren)) + { + context.CancellationToken.ThrowIfCancellationRequested(); + } + + // now search structure trivia + foreach (var candidate in context.Node.DescendantNodesAndSelf(descendIntoChildren: n => !IsCrefCandidate(n), descendIntoTrivia: true)) + { + context.CancellationToken.ThrowIfCancellationRequested(); + + if (IsCrefCandidate(candidate) && + TrySimplifyTypeNameExpression(context.SemanticModel, candidate, context.Options, out diagnostic, context.CancellationToken)) + { + context.ReportDiagnostic(diagnostic); + } + } + } + + internal static bool IsCandidate(SyntaxNode node) + { + return IsRegularCandidate(node) || IsCrefCandidate(node); + } + + private static bool IsRegularCandidate(SyntaxNode node) + { + return node != null && s_kindsOfInterest.Contains(node.Kind()); + } + + private static bool IsCrefCandidate(SyntaxNode node) + { + return node is QualifiedCrefSyntax; + } + + protected sealed override bool CanSimplifyTypeNameExpressionCore(SemanticModel model, SyntaxNode node, OptionSet optionSet, out TextSpan issueSpan, out string diagnosticId, CancellationToken cancellationToken) + { + return CanSimplifyTypeNameExpression(model, node, optionSet, out issueSpan, out diagnosticId, cancellationToken); + } + + internal static bool CanSimplifyTypeNameExpression(SemanticModel model, SyntaxNode node, OptionSet optionSet, out TextSpan issueSpan, out string diagnosticId, CancellationToken cancellationToken) + { + issueSpan = default(TextSpan); + diagnosticId = IDEDiagnosticIds.SimplifyNamesDiagnosticId; + + // For Crefs, currently only Qualified Crefs needs to be handled separately + if (node.Kind() == SyntaxKind.QualifiedCref) + { + if (node.ContainsDiagnostics) + { + return false; + } + + var crefSyntax = (CrefSyntax)node; + + CrefSyntax replacementNode; + if (!crefSyntax.TryReduceOrSimplifyExplicitName (model, out replacementNode, out issueSpan, optionSet, cancellationToken)) + { + return false; + } + } + else + { + var expression = (ExpressionSyntax)node; + if (expression.ContainsDiagnostics) + { + return false; + } + + // in case of an As or Is expression we need to handle the binary expression, because it might be + // required to add parenthesis around the expression. Adding the parenthesis is done in the CSharpNameSimplifier.Rewriter + var expressionToCheck = expression.Kind() == SyntaxKind.AsExpression || expression.Kind() == SyntaxKind.IsExpression + ? ((BinaryExpressionSyntax)expression).Right + : expression; + + ExpressionSyntax replacementSyntax; + if (!expressionToCheck.TryReduceOrSimplifyExplicitName(model, out replacementSyntax, out issueSpan, optionSet, cancellationToken)) + { + return false; + } + + if (expression.Kind() == SyntaxKind.SimpleMemberAccessExpression) + { + var memberAccess = (MemberAccessExpressionSyntax)expression; + diagnosticId = memberAccess.Expression.Kind() == SyntaxKind.ThisExpression ? + IDEDiagnosticIds.SimplifyThisOrMeDiagnosticId : + IDEDiagnosticIds.SimplifyMemberAccessDiagnosticId; + } + } + + return true; + } + + protected override string GetLanguageName() + { + return LanguageNames.CSharp; + } + } +} |