File: Compilation\ParseOptions.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis
{
    /// <summary>
    /// Represents parse options common to C# and VB.
    /// </summary>
    public abstract class ParseOptions
    {
        private readonly Lazy<ImmutableArray<Diagnostic>> _lazyErrors;
 
        /// <summary>
        /// Specifies whether to parse as regular code files, script files or interactive code.
        /// </summary>
        public SourceCodeKind Kind { get; protected set; }
 
        /// <summary>
        /// Gets the specified source code kind, which is the value that was specified in
        /// the call to the constructor, or modified using the <see cref="WithKind(SourceCodeKind)"/> method.
        /// </summary>
        public SourceCodeKind SpecifiedKind { get; protected set; }
 
        /// <summary>
        /// Gets a value indicating whether the documentation comments are parsed and analyzed.
        /// </summary>
        public DocumentationMode DocumentationMode { get; protected set; }
 
        internal ParseOptions(SourceCodeKind kind, DocumentationMode documentationMode)
        {
            this.SpecifiedKind = kind;
            this.Kind = kind.MapSpecifiedToEffectiveKind();
            this.DocumentationMode = documentationMode;
 
            _lazyErrors = new Lazy<ImmutableArray<Diagnostic>>(() =>
            {
                var builder = ArrayBuilder<Diagnostic>.GetInstance();
                ValidateOptions(builder);
                return builder.ToImmutableAndFree();
            });
        }
 
        /// <summary>
        /// Gets the source language ("C#" or "Visual Basic").
        /// </summary>
        public abstract string Language { get; }
 
        /// <summary>
        /// Errors collection related to an incompatible set of parse options
        /// </summary>
        public ImmutableArray<Diagnostic> Errors
        {
            get { return _lazyErrors.Value; }
        }
 
        /// <summary>
        /// Creates a new options instance with the specified source code kind.
        /// </summary>
        public ParseOptions WithKind(SourceCodeKind kind)
        {
            return CommonWithKind(kind);
        }
 
        /// <summary>
        /// Performs validation of options compatibilities and generates diagnostics if needed
        /// </summary>
        internal abstract void ValidateOptions(ArrayBuilder<Diagnostic> builder);
 
        internal void ValidateOptions(ArrayBuilder<Diagnostic> builder, CommonMessageProvider messageProvider)
        {
            // Validate SpecifiedKind not Kind, to catch deprecated specified kinds:
            if (!SpecifiedKind.IsValid())
            {
                builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_BadSourceCodeKind, Location.None, SpecifiedKind.ToString()));
            }
 
            if (!DocumentationMode.IsValid())
            {
                builder.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_BadDocumentationMode, Location.None, DocumentationMode.ToString()));
            }
        }
 
        // It was supposed to be a protected implementation detail. 
        // The "pattern" we have for these is the public With* method is the only public callable one, 
        // and that forwards to the protected Common* like all the other methods in the class. 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public abstract ParseOptions CommonWithKind(SourceCodeKind kind);
 
        /// <summary>
        /// Creates a new options instance with the specified documentation mode.
        /// </summary>
        public ParseOptions WithDocumentationMode(DocumentationMode documentationMode)
        {
            return CommonWithDocumentationMode(documentationMode);
        }
 
        protected abstract ParseOptions CommonWithDocumentationMode(DocumentationMode documentationMode);
 
        /// <summary>
        /// Enable some experimental language features for testing.
        /// </summary>
        public ParseOptions WithFeatures(IEnumerable<KeyValuePair<string, string>> features)
        {
            return CommonWithFeatures(features);
        }
 
        protected abstract ParseOptions CommonWithFeatures(IEnumerable<KeyValuePair<string, string>> features);
 
        /// <summary>
        /// Returns the experimental features.
        /// </summary>
        public abstract IReadOnlyDictionary<string, string> Features
        {
            get;
        }
 
        /// <summary>
        /// Names of defined preprocessor symbols.
        /// </summary>
        public abstract IEnumerable<string> PreprocessorSymbolNames { get; }
 
        public abstract override bool Equals(object? obj);
 
        protected bool EqualsHelper([NotNullWhen(true)] ParseOptions? other)
        {
            if (object.ReferenceEquals(other, null))
            {
                return false;
            }
 
            return
                this.SpecifiedKind == other.SpecifiedKind &&
                this.DocumentationMode == other.DocumentationMode &&
                this.Features.SequenceEqual(other.Features) &&
                (this.PreprocessorSymbolNames == null ? other.PreprocessorSymbolNames == null : this.PreprocessorSymbolNames.SequenceEqual(other.PreprocessorSymbolNames, StringComparer.Ordinal));
        }
 
        public abstract override int GetHashCode();
 
        protected int GetHashCodeHelper()
        {
            return
                Hash.Combine((int)this.SpecifiedKind,
                Hash.Combine((int)this.DocumentationMode,
                Hash.Combine(HashFeatures(this.Features),
                Hash.Combine(Hash.CombineValues(this.PreprocessorSymbolNames, StringComparer.Ordinal), 0))));
        }
 
        private static int HashFeatures(IReadOnlyDictionary<string, string> features)
        {
            int value = 0;
            foreach (var kv in features)
            {
                value = Hash.Combine(kv.Key.GetHashCode(),
                        Hash.Combine(kv.Value.GetHashCode(), value));
            }
 
            return value;
        }
 
        public static bool operator ==(ParseOptions? left, ParseOptions? right)
        {
            return object.Equals(left, right);
        }
 
        public static bool operator !=(ParseOptions? left, ParseOptions? right)
        {
            return !object.Equals(left, right);
        }
    }
}