<commit_before>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

#nullable enable

using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Classification.Classifiers;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;

namespace Microsoft.CodeAnalysis.CSharp.Classification.Classifiers
{
    internal class DiscardSyntaxClassifier : AbstractSyntaxClassifier
    {
        public override void AddClassifications(
           Workspace workspace,
           SyntaxNode syntax,
           SemanticModel semanticModel,
           ArrayBuilder<ClassifiedSpan> result,
           CancellationToken cancellationToken)
        {
            if (syntax.IsKind(SyntaxKind.DiscardDesignation) || syntax.IsKind(SyntaxKind.DiscardPattern))
            {
                result.Add(new ClassifiedSpan(syntax.Span, ClassificationTypeNames.Keyword));
                return;
            }

            var _ =
                TryClassifyCallArgument(syntax, semanticModel, result, cancellationToken) ||
                TryClassifyLambdaParameter(syntax, semanticModel, result, cancellationToken) ||
                TryClassifySymbol(syntax, semanticModel, result, cancellationToken);
        }

        private bool TryClassifyCallArgument(SyntaxNode syntax, SemanticModel semanticModel, ArrayBuilder<ClassifiedSpan> result, CancellationToken cancellationToken)
        {
            if (!(syntax is ArgumentSyntax argument))
            {
                return false;
            }

            // in arguments we don't need to go on if the out is missing or the argument isn't inside a tuple
            if (!argument.RefOrOutKeyword.IsKind(SyntaxKind.OutKeyword) &&
                !argument.IsParentKind(SyntaxKind.TupleExpression))
            {
                return false;
            }

            var operation = semanticModel.GetOperation(argument.Expression, cancellationToken);
            if (!(operation is IDiscardOperation discardOperation))
            {
                return false;
            }

            switch (discardOperation.Syntax)
            {
                case DeclarationExpressionSyntax decl:
                    result.Add(new ClassifiedSpan(decl.Designation.Span, ClassificationTypeNames.Keyword));
                    return true;
                case IdentifierNameSyntax discard:
                    result.Add(new ClassifiedSpan(discard.Span, ClassificationTypeNames.Keyword));
                    return true;
                default:
                    return false;
            }
        }

        private bool TryClassifyLambdaParameter(SyntaxNode syntax, SemanticModel semanticModel, ArrayBuilder<ClassifiedSpan> result, CancellationToken cancellationToken)
        {
            if (!(syntax is LambdaExpressionSyntax lambda))
            {
                return false;
            }

            var symbolInfo = semanticModel.GetSymbolInfo(lambda, cancellationToken);
            var symbol = symbolInfo.Symbol as IMethodSymbol;

            if (symbol == null)
            {
                return false;
            }

            foreach (var parameter in symbol.Parameters)
            {
                if (parameter.IsDiscard)
                {
                    result.Add(new ClassifiedSpan(parameter.Locations[0].SourceSpan, ClassificationTypeNames.Keyword));
                }
            }

            return true;
        }

        private bool TryClassifySymbol(SyntaxNode syntax, SemanticModel semanticModel, ArrayBuilder<ClassifiedSpan> result, CancellationToken cancellationToken)
        {
            var symbolInfo = semanticModel.GetSymbolInfo(syntax, cancellationToken);
            var symbol = symbolInfo.Symbol;

            if (symbol?.Kind == SymbolKind.Discard)
            {
                result.Add(new ClassifiedSpan(syntax.Span, ClassificationTypeNames.Keyword));
                return true;
            }

            return false;
        }
    }
}<commit_msg>Refactoring of the DiscardSyntaxClassifier: - Got rid of TryClassifyCallArgument which was covered by TryClassifySymbol after resolving all the syntax on the stack - Expressed code flow as a switch rather than a chaind short-circuit or - declared appropriate SyntaxNodeTypes so the classifier isn't run on everything<commit_after>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

#nullable enable

using System;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Classification.Classifiers;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;

namespace Microsoft.CodeAnalysis.CSharp.Classification.Classifiers
{
    internal class DiscardSyntaxClassifier : AbstractSyntaxClassifier
    {
        public override ImmutableArray<Type> SyntaxNodeTypes { get; } = ImmutableArray.Create(
            typeof(DiscardDesignationSyntax),
            typeof(DiscardPatternSyntax),
            typeof(LambdaExpressionSyntax),
            typeof(IdentifierNameSyntax));

        public override void AddClassifications(
           Workspace workspace,
           SyntaxNode syntax,
           SemanticModel semanticModel,
           ArrayBuilder<ClassifiedSpan> result,
           CancellationToken cancellationToken)
        {
            if (syntax.IsKind(SyntaxKind.DiscardDesignation) || syntax.IsKind(SyntaxKind.DiscardPattern))
            {
                result.Add(new ClassifiedSpan(syntax.Span, ClassificationTypeNames.Keyword));
                return;
            }

            var symbolInfo = semanticModel.GetSymbolInfo(syntax, cancellationToken);

            switch (syntax)
            {
                case LambdaExpressionSyntax _:
                    ClassifyLambdaParameter(symbolInfo, result);
                    break;

                default:
                    ClassifySymbol(syntax, symbolInfo, result);
                    break;
            };
        }

        private void ClassifyLambdaParameter(SymbolInfo symbolInfo, ArrayBuilder<ClassifiedSpan> result)
        {
            // classify lambda parameters of the forms 
            // (int _, int _) => ... 
            // or 
            // (_, _) => ...
            // which aren't covered by TryClassifySymbol

            if (!(symbolInfo.Symbol is IMethodSymbol symbol))
            {
                return;
            }

            foreach (var parameter in symbol.Parameters)
            {
                if (parameter.IsDiscard)
                {
                    result.Add(new ClassifiedSpan(parameter.Locations[0].SourceSpan, ClassificationTypeNames.Keyword));
                }
            }
        }

        private void ClassifySymbol(SyntaxNode syntax, SymbolInfo symbolInfo, ArrayBuilder<ClassifiedSpan> result)
        {
            // the general case for all discarding symbols

            if (symbolInfo.Symbol?.Kind == SymbolKind.Discard)
            {
                result.Add(new ClassifiedSpan(syntax.Span, ClassificationTypeNames.Keyword));
            }
        }
    }
}
{'fraction_non_alphanumeric': 0.06790536024595642, 'fraction_numerical': 0.0008020318139286192, 'mean_word_length': 2.109725685785536, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4765552', 'n_tokens_mistral': 1757, 'n_tokens_neox': 1663, 'n_words': 416}