// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Microsoft.DiaSymReader;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
///
/// The compilation object is an immutable representation of a single invocation of the
/// compiler. Although immutable, a compilation is also on-demand, and will realize and cache
/// data as necessary. A compilation can produce a new compilation from existing compilation
/// with the application of small deltas. In many cases, it is more efficient than creating a
/// new compilation from scratch, as the new compilation can reuse information from the old
/// compilation.
///
public abstract partial class Compilation
{
///
/// Returns true if this is a case sensitive compilation, false otherwise. Case sensitivity
/// affects compilation features such as name lookup as well as choosing what names to emit
/// when there are multiple different choices (for example between a virtual method and an
/// override).
///
public abstract bool IsCaseSensitive { get; }
///
/// Used for test purposes only to emulate missing members.
///
private SmallDictionary _lazyMakeWellKnownTypeMissingMap;
///
/// Used for test purposes only to emulate missing members.
///
private SmallDictionary _lazyMakeMemberMissingMap;
private readonly IReadOnlyDictionary _features;
public ScriptCompilationInfo ScriptCompilationInfo => CommonScriptCompilationInfo;
internal abstract ScriptCompilationInfo CommonScriptCompilationInfo { get; }
internal Compilation(
string name,
ImmutableArray references,
IReadOnlyDictionary features,
bool isSubmission,
AsyncQueue eventQueue)
{
Debug.Assert(!references.IsDefault);
Debug.Assert(features != null);
this.AssemblyName = name;
this.ExternalReferences = references;
this.EventQueue = eventQueue;
_lazySubmissionSlotIndex = isSubmission ? SubmissionSlotIndexToBeAllocated : SubmissionSlotIndexNotApplicable;
_features = features;
}
protected static IReadOnlyDictionary SyntaxTreeCommonFeatures(IEnumerable trees)
{
IReadOnlyDictionary set = null;
foreach (var tree in trees)
{
var treeFeatures = tree.Options.Features;
if (set == null)
{
set = treeFeatures;
}
else
{
if ((object)set != treeFeatures && !set.SetEquals(treeFeatures))
{
throw new ArgumentException(CodeAnalysisResources.InconsistentSyntaxTreeFeature, nameof(trees));
}
}
}
if (set == null)
{
// Edge case where there are no syntax trees
set = ImmutableDictionary.Empty;
}
return set;
}
internal abstract AnalyzerDriver AnalyzerForLanguage(ImmutableArray analyzers, AnalyzerManager analyzerManager);
///
/// Gets the source language ("C#" or "Visual Basic").
///
public abstract string Language { get; }
internal static void ValidateScriptCompilationParameters(Compilation previousScriptCompilation, Type returnType, ref Type globalsType)
{
if (globalsType != null && !IsValidHostObjectType(globalsType))
{
throw new ArgumentException(CodeAnalysisResources.ReturnTypeCannotBeValuePointerbyRefOrOpen, nameof(globalsType));
}
if (returnType != null && !IsValidSubmissionReturnType(returnType))
{
throw new ArgumentException(CodeAnalysisResources.ReturnTypeCannotBeVoidByRefOrOpen, nameof(returnType));
}
if (previousScriptCompilation != null)
{
if (globalsType == null)
{
globalsType = previousScriptCompilation.HostObjectType;
}
else if (globalsType != previousScriptCompilation.HostObjectType)
{
throw new ArgumentException(CodeAnalysisResources.TypeMustBeSameAsHostObjectTypeOfPreviousSubmission, nameof(globalsType));
}
// Force the previous submission to be analyzed. This is required for anonymous types unification.
if (previousScriptCompilation.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error))
{
throw new InvalidOperationException(CodeAnalysisResources.PreviousSubmissionHasErrors);
}
}
}
///
/// Checks options passed to submission compilation constructor.
/// Throws an exception if the options are not applicable to submissions.
///
internal static void CheckSubmissionOptions(CompilationOptions options)
{
if (options == null)
{
return;
}
if (options.OutputKind.IsValid() && options.OutputKind != OutputKind.DynamicallyLinkedLibrary)
{
throw new ArgumentException(CodeAnalysisResources.InvalidOutputKindForSubmission, nameof(options));
}
if (options.CryptoKeyContainer != null ||
options.CryptoKeyFile != null ||
options.DelaySign != null ||
!options.CryptoPublicKey.IsEmpty ||
(options.DelaySign == true && options.PublicSign))
{
throw new ArgumentException(CodeAnalysisResources.InvalidCompilationOptions, nameof(options));
}
}
///
/// Creates a new compilation equivalent to this one with different symbol instances.
///
public Compilation Clone()
{
return CommonClone();
}
protected abstract Compilation CommonClone();
///
/// Returns a new compilation with a given event queue.
///
internal abstract Compilation WithEventQueue(AsyncQueue eventQueue);
///
/// Gets a new for the specified syntax tree.
///
/// The specified syntax tree.
///
/// True if the SemanticModel should ignore accessibility rules when answering semantic questions.
///
public SemanticModel GetSemanticModel(SyntaxTree syntaxTree, bool ignoreAccessibility = false)
{
return CommonGetSemanticModel(syntaxTree, ignoreAccessibility);
}
protected abstract SemanticModel CommonGetSemanticModel(SyntaxTree syntaxTree, bool ignoreAccessibility);
///
/// Returns a new INamedTypeSymbol representing an error type with the given name and arity
/// in the given optional container.
///
public INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (arity < 0)
{
throw new ArgumentException($"{nameof(arity)} must be >= 0", nameof(arity));
}
return CommonCreateErrorTypeSymbol(container, name, arity);
}
protected abstract INamedTypeSymbol CommonCreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity);
///
/// Returns a new INamespaceSymbol representing an error (missing) namespace with the given name.
///
public INamespaceSymbol CreateErrorNamespaceSymbol(INamespaceSymbol container, string name)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return CommonCreateErrorNamespaceSymbol(container, name);
}
protected abstract INamespaceSymbol CommonCreateErrorNamespaceSymbol(INamespaceSymbol container, string name);
#region Name
internal const string UnspecifiedModuleAssemblyName = "?";
///
/// Simple assembly name, or null if not specified.
///
///
/// The name is used for determining internals-visible-to relationship with referenced assemblies.
///
/// If the compilation represents an assembly the value of is its simple name.
///
/// Unless specifies otherwise the module name
/// written to metadata is with an extension based upon .
///
public string AssemblyName { get; }
internal void CheckAssemblyName(DiagnosticBag diagnostics)
{
// We could only allow name == null if OutputKind is Module.
// However, it does no harm that we allow name == null for assemblies as well, so we don't enforce it.
if (this.AssemblyName != null)
{
MetadataHelpers.CheckAssemblyOrModuleName(this.AssemblyName, MessageProvider, MessageProvider.ERR_BadAssemblyName, diagnostics);
}
}
internal string MakeSourceAssemblySimpleName()
{
return AssemblyName ?? UnspecifiedModuleAssemblyName;
}
internal string MakeSourceModuleName()
{
return Options.ModuleName ??
(AssemblyName != null ? AssemblyName + Options.OutputKind.GetDefaultExtension() : UnspecifiedModuleAssemblyName);
}
///
/// Creates a compilation with the specified assembly name.
///
/// The new assembly name.
/// A new compilation.
public Compilation WithAssemblyName(string assemblyName)
{
return CommonWithAssemblyName(assemblyName);
}
protected abstract Compilation CommonWithAssemblyName(string outputName);
#endregion
#region Options
///
/// Gets the options the compilation was created with.
///
public CompilationOptions Options { get { return CommonOptions; } }
protected abstract CompilationOptions CommonOptions { get; }
///
/// Creates a new compilation with the specified compilation options.
///
/// The new options.
/// A new compilation.
public Compilation WithOptions(CompilationOptions options)
{
return CommonWithOptions(options);
}
protected abstract Compilation CommonWithOptions(CompilationOptions options);
#endregion
#region Submissions
// An index in the submission slot array. Allocated lazily in compilation phase based upon the slot index of the previous submission.
// Special values:
// -1 ... neither this nor previous submissions in the chain allocated a slot (the submissions don't contain code)
// -2 ... the slot of this submission hasn't been determined yet
// -3 ... this is not a submission compilation
private int _lazySubmissionSlotIndex;
private const int SubmissionSlotIndexNotApplicable = -3;
private const int SubmissionSlotIndexToBeAllocated = -2;
///
/// True if the compilation represents an interactive submission.
///
internal bool IsSubmission
{
get
{
return _lazySubmissionSlotIndex != SubmissionSlotIndexNotApplicable;
}
}
///
/// The previous submission, if any, or null.
///
private Compilation PreviousSubmission
{
get
{
return ScriptCompilationInfo?.PreviousScriptCompilation;
}
}
///
/// Gets or allocates a runtime submission slot index for this compilation.
///
/// Non-negative integer if this is a submission and it or a previous submission contains code, negative integer otherwise.
internal int GetSubmissionSlotIndex()
{
if (_lazySubmissionSlotIndex == SubmissionSlotIndexToBeAllocated)
{
// TODO (tomat): remove recursion
int lastSlotIndex = ScriptCompilationInfo.PreviousScriptCompilation?.GetSubmissionSlotIndex() ?? 0;
_lazySubmissionSlotIndex = HasCodeToEmit() ? lastSlotIndex + 1 : lastSlotIndex;
}
return _lazySubmissionSlotIndex;
}
// The type of interactive submission result requested by the host, or null if this compilation doesn't represent a submission.
//
// The type is resolved to a symbol when the Script's instance ctor symbol is constructed. The symbol needs to be resolved against
// the references of this compilation.
//
// Consider (tomat): As an alternative to Reflection Type we could hold onto any piece of information that lets us
// resolve the type symbol when needed.
///
/// The type object that represents the type of submission result the host requested.
///
internal Type SubmissionReturnType => ScriptCompilationInfo?.ReturnTypeOpt;
internal static bool IsValidSubmissionReturnType(Type type)
{
return !(type == typeof(void) || type.IsByRef || type.GetTypeInfo().ContainsGenericParameters);
}
///
/// The type of the globals object or null if not specified for this compilation.
///
internal Type HostObjectType => ScriptCompilationInfo?.GlobalsType;
internal static bool IsValidHostObjectType(Type type)
{
var info = type.GetTypeInfo();
return !(info.IsValueType || info.IsPointer || info.IsByRef || info.ContainsGenericParameters);
}
internal abstract bool HasSubmissionResult();
public Compilation WithScriptCompilationInfo(ScriptCompilationInfo info) => CommonWithScriptCompilationInfo(info);
protected abstract Compilation CommonWithScriptCompilationInfo(ScriptCompilationInfo info);
#endregion
#region Syntax Trees
///
/// Gets the syntax trees (parsed from source code) that this compilation was created with.
///
public IEnumerable SyntaxTrees { get { return CommonSyntaxTrees; } }
protected abstract IEnumerable CommonSyntaxTrees { get; }
///
/// Creates a new compilation with additional syntax trees.
///
/// The new syntax trees.
/// A new compilation.
public Compilation AddSyntaxTrees(params SyntaxTree[] trees)
{
return CommonAddSyntaxTrees(trees);
}
///
/// Creates a new compilation with additional syntax trees.
///
/// The new syntax trees.
/// A new compilation.
public Compilation AddSyntaxTrees(IEnumerable trees)
{
return CommonAddSyntaxTrees(trees);
}
protected abstract Compilation CommonAddSyntaxTrees(IEnumerable trees);
///
/// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees
/// added later.
///
/// The new syntax trees.
/// A new compilation.
public Compilation RemoveSyntaxTrees(params SyntaxTree[] trees)
{
return CommonRemoveSyntaxTrees(trees);
}
///
/// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees
/// added later.
///
/// The new syntax trees.
/// A new compilation.
public Compilation RemoveSyntaxTrees(IEnumerable trees)
{
return CommonRemoveSyntaxTrees(trees);
}
protected abstract Compilation CommonRemoveSyntaxTrees(IEnumerable trees);
///
/// Creates a new compilation without any syntax trees. Preserves metadata info for use with
/// trees added later.
///
public Compilation RemoveAllSyntaxTrees()
{
return CommonRemoveAllSyntaxTrees();
}
protected abstract Compilation CommonRemoveAllSyntaxTrees();
///
/// Creates a new compilation with an old syntax tree replaced with a new syntax tree.
/// Reuses metadata from old compilation object.
///
/// The new tree.
/// The old tree.
/// A new compilation.
public Compilation ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree)
{
return CommonReplaceSyntaxTree(oldTree, newTree);
}
protected abstract Compilation CommonReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree);
///
/// Returns true if this compilation contains the specified tree. False otherwise.
///
/// A syntax tree.
public bool ContainsSyntaxTree(SyntaxTree syntaxTree)
{
return CommonContainsSyntaxTree(syntaxTree);
}
protected abstract bool CommonContainsSyntaxTree(SyntaxTree syntaxTree);
///
/// The event queue that this compilation was created with.
///
internal readonly AsyncQueue EventQueue;
#endregion
#region References
internal static ImmutableArray ValidateReferences(IEnumerable references)
where T : CompilationReference
{
var result = references.AsImmutableOrEmpty();
for (int i = 0; i < result.Length; i++)
{
var reference = result[i];
if (reference == null)
{
throw new ArgumentNullException($"{nameof(references)}[{i}]");
}
var peReference = reference as PortableExecutableReference;
if (peReference == null && !(reference is T))
{
Debug.Assert(reference is UnresolvedMetadataReference || reference is CompilationReference);
throw new ArgumentException(string.Format(CodeAnalysisResources.ReferenceOfTypeIsInvalid1, reference.GetType()),
$"{nameof(references)}[{i}]");
}
}
return result;
}
internal CommonReferenceManager GetBoundReferenceManager()
{
return CommonGetBoundReferenceManager();
}
internal abstract CommonReferenceManager CommonGetBoundReferenceManager();
///
/// Metadata references passed to the compilation constructor.
///
public ImmutableArray ExternalReferences { get; }
///
/// Unique metadata references specified via #r directive in the source code of this compilation.
///
public abstract ImmutableArray DirectiveReferences { get; }
///
/// All reference directives used in this compilation.
///
internal abstract IEnumerable ReferenceDirectives { get; }
///
/// Maps values of #r references to resolved metadata references.
///
internal abstract IDictionary<(string path, string content), MetadataReference> ReferenceDirectiveMap { get; }
///
/// All metadata references -- references passed to the compilation
/// constructor as well as references specified via #r directives.
///
public IEnumerable References
{
get
{
foreach (var reference in ExternalReferences)
{
yield return reference;
}
foreach (var reference in DirectiveReferences)
{
yield return reference;
}
}
}
///
/// Creates a metadata reference for this compilation.
///
///
/// Optional aliases that can be used to refer to the compilation root namespace via extern alias directive.
///
///
/// Embed the COM types from the reference so that the compiled
/// application no longer requires a primary interop assembly (PIA).
///
public abstract CompilationReference ToMetadataReference(ImmutableArray aliases = default(ImmutableArray), bool embedInteropTypes = false);
///
/// Creates a new compilation with the specified references.
///
///
/// The new references.
///
/// A new compilation.
public Compilation WithReferences(IEnumerable newReferences)
{
return this.CommonWithReferences(newReferences);
}
///
/// Creates a new compilation with the specified references.
///
/// The new references.
/// A new compilation.
public Compilation WithReferences(params MetadataReference[] newReferences)
{
return this.WithReferences((IEnumerable)newReferences);
}
///
/// Creates a new compilation with the specified references.
///
protected abstract Compilation CommonWithReferences(IEnumerable newReferences);
///
/// Creates a new compilation with additional metadata references.
///
/// The new references.
/// A new compilation.
public Compilation AddReferences(params MetadataReference[] references)
{
return AddReferences((IEnumerable)references);
}
///
/// Creates a new compilation with additional metadata references.
///
/// The new references.
/// A new compilation.
public Compilation AddReferences(IEnumerable references)
{
if (references == null)
{
throw new ArgumentNullException(nameof(references));
}
if (references.IsEmpty())
{
return this;
}
return CommonWithReferences(this.ExternalReferences.Union(references));
}
///
/// Creates a new compilation without the specified metadata references.
///
/// The new references.
/// A new compilation.
public Compilation RemoveReferences(params MetadataReference[] references)
{
return RemoveReferences((IEnumerable)references);
}
///
/// Creates a new compilation without the specified metadata references.
///
/// The new references.
/// A new compilation.
public Compilation RemoveReferences(IEnumerable references)
{
if (references == null)
{
throw new ArgumentNullException(nameof(references));
}
if (references.IsEmpty())
{
return this;
}
var refSet = new HashSet(this.ExternalReferences);
//EDMAURER if AddingReferences accepts duplicates, then a consumer supplying a list with
//duplicates to add will not know exactly which to remove. Let them supply a list with
//duplicates here.
foreach (var r in references.Distinct())
{
if (!refSet.Remove(r))
{
throw new ArgumentException(string.Format(CodeAnalysisResources.MetadataRefNotFoundToRemove1, r),
nameof(references));
}
}
return CommonWithReferences(refSet);
}
///
/// Creates a new compilation without any metadata references.
///
public Compilation RemoveAllReferences()
{
return CommonWithReferences(SpecializedCollections.EmptyEnumerable());
}
///
/// Creates a new compilation with an old metadata reference replaced with a new metadata
/// reference.
///
/// The new reference.
/// The old reference.
/// A new compilation.
public Compilation ReplaceReference(MetadataReference oldReference, MetadataReference newReference)
{
if (oldReference == null)
{
throw new ArgumentNullException(nameof(oldReference));
}
if (newReference == null)
{
return this.RemoveReferences(oldReference);
}
return this.RemoveReferences(oldReference).AddReferences(newReference);
}
///
/// Gets the or for a metadata reference used to create this
/// compilation.
///
/// The target reference.
///
/// Assembly or module symbol corresponding to the given reference or null if there is none.
///
public ISymbol GetAssemblyOrModuleSymbol(MetadataReference reference)
{
return CommonGetAssemblyOrModuleSymbol(reference);
}
protected abstract ISymbol CommonGetAssemblyOrModuleSymbol(MetadataReference reference);
///
/// Gets the that corresponds to the assembly symbol.
///
/// The target symbol.
public MetadataReference GetMetadataReference(IAssemblySymbol assemblySymbol)
{
return GetBoundReferenceManager().GetMetadataReference(assemblySymbol);
}
///
/// Assembly identities of all assemblies directly referenced by this compilation.
///
///
/// Includes identities of references passed in the compilation constructor
/// as well as those specified via directives in source code.
///
public abstract IEnumerable ReferencedAssemblyNames { get; }
#endregion
#region Symbols
///
/// The that represents the assembly being created.
///
public IAssemblySymbol Assembly { get { return CommonAssembly; } }
protected abstract IAssemblySymbol CommonAssembly { get; }
///
/// Gets the for the module being created by compiling all of
/// the source code.
///
public IModuleSymbol SourceModule { get { return CommonSourceModule; } }
protected abstract IModuleSymbol CommonSourceModule { get; }
///
/// The root namespace that contains all namespaces and types defined in source code or in
/// referenced metadata, merged into a single namespace hierarchy.
///
public INamespaceSymbol GlobalNamespace { get { return CommonGlobalNamespace; } }
protected abstract INamespaceSymbol CommonGlobalNamespace { get; }
///
/// Gets the corresponding compilation namespace for the specified module or assembly namespace.
///
public INamespaceSymbol GetCompilationNamespace(INamespaceSymbol namespaceSymbol)
{
return CommonGetCompilationNamespace(namespaceSymbol);
}
protected abstract INamespaceSymbol CommonGetCompilationNamespace(INamespaceSymbol namespaceSymbol);
internal abstract CommonAnonymousTypeManager CommonAnonymousTypeManager { get; }
///
/// Returns the Main method that will serves as the entry point of the assembly, if it is
/// executable (and not a script).
///
public IMethodSymbol GetEntryPoint(CancellationToken cancellationToken)
{
return CommonGetEntryPoint(cancellationToken);
}
protected abstract IMethodSymbol CommonGetEntryPoint(CancellationToken cancellationToken);
///
/// Get the symbol for the predefined type from the Cor Library referenced by this
/// compilation.
///
public INamedTypeSymbol GetSpecialType(SpecialType specialType)
{
return CommonGetSpecialType(specialType);
}
///
/// Get the symbol for the predefined type member from the COR Library referenced by this compilation.
///
internal abstract ISymbol CommonGetSpecialTypeMember(SpecialMember specialMember);
///
/// Returns true if the type is System.Type.
///
internal abstract bool IsSystemTypeReference(ITypeSymbol type);
protected abstract INamedTypeSymbol CommonGetSpecialType(SpecialType specialType);
///
/// Lookup member declaration in well known type used by this Compilation.
///
internal abstract ISymbol CommonGetWellKnownTypeMember(WellKnownMember member);
///
/// Returns true if the specified type is equal to or derives from System.Attribute well-known type.
///
internal abstract bool IsAttributeType(ITypeSymbol type);
///
/// The INamedTypeSymbol for the .NET System.Object type, which could have a TypeKind of
/// Error if there was no COR Library in this Compilation.
///
public INamedTypeSymbol ObjectType { get { return CommonObjectType; } }
protected abstract INamedTypeSymbol CommonObjectType { get; }
///
/// The TypeSymbol for the type 'dynamic' in this Compilation.
///
public ITypeSymbol DynamicType { get { return CommonDynamicType; } }
protected abstract ITypeSymbol CommonDynamicType { get; }
///
/// A symbol representing the implicit Script class. This is null if the class is not
/// defined in the compilation.
///
public INamedTypeSymbol ScriptClass { get { return CommonScriptClass; } }
protected abstract INamedTypeSymbol CommonScriptClass { get; }
///
/// Resolves a symbol that represents script container (Script class). Uses the
/// full name of the container class stored in to find the symbol.
///
/// The Script class symbol or null if it is not defined.
protected INamedTypeSymbol CommonBindScriptClass()
{
string scriptClassName = this.Options.ScriptClassName ?? "";
string[] parts = scriptClassName.Split('.');
INamespaceSymbol container = this.SourceModule.GlobalNamespace;
for (int i = 0; i < parts.Length - 1; i++)
{
INamespaceSymbol next = container.GetNestedNamespace(parts[i]);
if (next == null)
{
AssertNoScriptTrees();
return null;
}
container = next;
}
foreach (INamedTypeSymbol candidate in container.GetTypeMembers(parts[parts.Length - 1]))
{
if (candidate.IsScriptClass)
{
return candidate;
}
}
AssertNoScriptTrees();
return null;
}
[Conditional("DEBUG")]
private void AssertNoScriptTrees()
{
foreach (var tree in this.SyntaxTrees)
{
Debug.Assert(tree.Options.Kind != SourceCodeKind.Script);
}
}
///
/// Returns a new ArrayTypeSymbol representing an array type tied to the base types of the
/// COR Library in this Compilation.
///
public IArrayTypeSymbol CreateArrayTypeSymbol(ITypeSymbol elementType, int rank = 1)
{
return CommonCreateArrayTypeSymbol(elementType, rank);
}
protected abstract IArrayTypeSymbol CommonCreateArrayTypeSymbol(ITypeSymbol elementType, int rank);
///
/// Returns a new PointerTypeSymbol representing a pointer type tied to a type in this
/// Compilation.
///
public IPointerTypeSymbol CreatePointerTypeSymbol(ITypeSymbol pointedAtType)
{
return CommonCreatePointerTypeSymbol(pointedAtType);
}
protected abstract IPointerTypeSymbol CommonCreatePointerTypeSymbol(ITypeSymbol elementType);
// PERF: ETW Traces show that analyzers may use this method frequently, often requesting
// the same symbol over and over again. XUnit analyzers, in particular, were consuming almost
// 1% of CPU time when building Roslyn itself. This is an extremely simple cache that evicts on
// hash code conflicts, but seems to do the trick. The size is mostly arbitrary. My guess
// is that there are maybe a couple dozen analyzers in the solution and each one has
// ~0-2 unique well-known types, and the chance of hash collision is very low.
private ConcurrentCache _getTypeCache =
new ConcurrentCache(50, ReferenceEqualityComparer.Instance);
///
/// Gets the type within the compilation's assembly and all referenced assemblies (other than
/// those that can only be referenced via an extern alias) using its canonical CLR metadata name.
///
/// Null if the type can't be found.
///
/// Since VB does not have the concept of extern aliases, it considers all referenced assemblies.
///
public INamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName)
{
if (!_getTypeCache.TryGetValue(fullyQualifiedMetadataName, out var val))
{
val = CommonGetTypeByMetadataName(fullyQualifiedMetadataName);
// Ignore if someone added the same value before us
_ = _getTypeCache.TryAdd(fullyQualifiedMetadataName, val);
}
return val;
}
protected abstract INamedTypeSymbol CommonGetTypeByMetadataName(string metadataName);
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
///
/// Returns a new INamedTypeSymbol with the given element types and (optional) element names.
///
public INamedTypeSymbol CreateTupleTypeSymbol(
ImmutableArray elementTypes,
ImmutableArray elementNames = default(ImmutableArray),
ImmutableArray elementLocations = default(ImmutableArray))
{
if (elementTypes.IsDefault)
{
throw new ArgumentNullException(nameof(elementTypes));
}
if (elementTypes.Length <= 1)
{
throw new ArgumentException(CodeAnalysisResources.TuplesNeedAtLeastTwoElements, nameof(elementNames));
}
elementNames = CheckTupleElementNames(elementTypes.Length, elementNames);
CheckTupleElementLocations(elementTypes.Length, elementLocations);
for (int i = 0, n = elementTypes.Length; i < n; i++)
{
if (elementTypes[i] == null)
{
throw new ArgumentNullException($"{nameof(elementTypes)}[{i}]");
}
if (!elementLocations.IsDefault && elementLocations[i] == null)
{
throw new ArgumentNullException($"{nameof(elementLocations)}[{i}]");
}
}
return CommonCreateTupleTypeSymbol(elementTypes, elementNames, elementLocations);
}
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
///
/// Check that if any names are provided, and their number matches the expected cardinality.
/// Returns a normalized version of the element names (empty array if all the names are null).
///
protected static ImmutableArray CheckTupleElementNames(int cardinality, ImmutableArray elementNames)
{
if (!elementNames.IsDefault)
{
if (elementNames.Length != cardinality)
{
throw new ArgumentException(CodeAnalysisResources.TupleElementNameCountMismatch, nameof(elementNames));
}
for (int i = 0; i < elementNames.Length; i++)
{
if (elementNames[i] == "")
{
throw new ArgumentException(CodeAnalysisResources.TupleElementNameEmpty, $"{nameof(elementNames)}[{i}]");
}
}
if (elementNames.All(n => n == null))
{
return default(ImmutableArray);
}
}
return elementNames;
}
protected static void CheckTupleElementLocations(
int cardinality,
ImmutableArray elementLocations)
{
if (!elementLocations.IsDefault)
{
if (elementLocations.Length != cardinality)
{
throw new ArgumentException(CodeAnalysisResources.TupleElementLocationCountMismatch, nameof(elementLocations));
}
}
}
protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol(
ImmutableArray elementTypes,
ImmutableArray elementNames,
ImmutableArray elementLocations);
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
///
/// Returns a new INamedTypeSymbol with the given underlying type and (optional) element names.
///
///
/// Since VB doesn't support tuples yet, this call will fail in a VB compilation.
/// Also, the underlying type needs to be tuple-compatible.
///
public INamedTypeSymbol CreateTupleTypeSymbol(
INamedTypeSymbol underlyingType,
ImmutableArray elementNames = default(ImmutableArray),
ImmutableArray elementLocations = default(ImmutableArray))
{
if ((object)underlyingType == null)
{
throw new ArgumentNullException(nameof(underlyingType));
}
return CommonCreateTupleTypeSymbol(
underlyingType, elementNames, elementLocations);
}
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol(
INamedTypeSymbol underlyingType,
ImmutableArray elementNames,
ImmutableArray elementLocations);
///
/// Returns a new anonymous type symbol with the given member types member names.
/// Anonymous type members will be readonly by default. Writable properties are
/// supported in VB and can be created by passing in in the
/// appropriate locations in .
///
/// Source locations can also be provided through
///
public INamedTypeSymbol CreateAnonymousTypeSymbol(
ImmutableArray memberTypes,
ImmutableArray memberNames,
ImmutableArray memberIsReadOnly = default(ImmutableArray),
ImmutableArray memberLocations = default(ImmutableArray))
{
if (memberTypes.IsDefault)
{
throw new ArgumentNullException(nameof(memberTypes));
}
if (memberNames.IsDefault)
{
throw new ArgumentNullException(nameof(memberNames));
}
if (memberTypes.Length != memberNames.Length)
{
throw new ArgumentException(string.Format(CodeAnalysisResources.AnonymousTypeMemberAndNamesCountMismatch2,
nameof(memberTypes), nameof(memberNames)));
}
if (!memberLocations.IsDefault && memberLocations.Length != memberTypes.Length)
{
throw new ArgumentException(string.Format(CodeAnalysisResources.AnonymousTypeArgumentCountMismatch2,
nameof(memberLocations), nameof(memberNames)));
}
if (!memberIsReadOnly.IsDefault && memberIsReadOnly.Length != memberTypes.Length)
{
throw new ArgumentException(string.Format(CodeAnalysisResources.AnonymousTypeArgumentCountMismatch2,
nameof(memberIsReadOnly), nameof(memberNames)));
}
for (int i = 0, n = memberTypes.Length; i < n; i++)
{
if (memberTypes[i] == null)
{
throw new ArgumentNullException($"{nameof(memberTypes)}[{i}]");
}
if (memberNames[i] == null)
{
throw new ArgumentNullException($"{nameof(memberNames)}[{i}]");
}
if (!memberLocations.IsDefault && memberLocations[i] == null)
{
throw new ArgumentNullException($"{nameof(memberLocations)}[{i}]");
}
}
return CommonCreateAnonymousTypeSymbol(memberTypes, memberNames, memberLocations, memberIsReadOnly);
}
protected abstract INamedTypeSymbol CommonCreateAnonymousTypeSymbol(
ImmutableArray memberTypes,
ImmutableArray memberNames,
ImmutableArray memberLocations,
ImmutableArray memberIsReadOnly);
///
/// Classifies a conversion from to according
/// to this compilation's programming language.
///
/// Source type of value to be converted
/// Destination type of value to be converted
/// A that classifies the conversion from the
/// type to the type.
public abstract CommonConversion ClassifyCommonConversion(ITypeSymbol source, ITypeSymbol destination);
///
/// Returns true if there is an implicit (C#) or widening (VB) conversion from
/// to . Returns false if
/// either or is null, or
/// if no such conversion exists.
///
public bool HasImplicitConversion(ITypeSymbol fromType, ITypeSymbol toType)
=> fromType != null && toType != null && this.ClassifyCommonConversion(fromType, toType).IsImplicit;
///
/// Checks if is accessible from within . An optional qualifier of type
/// is used to resolve protected access for instance members. All symbols are
/// required to be from this compilation or some assembly referenced () by this
/// compilation. is required to be an or .
///
///
/// Submissions can reference symbols from previous submissions and their referenced assemblies, even
/// though those references are missing from .
/// See https://github.com/dotnet/roslyn/issues/27356.
/// This implementation works around that by permitting symbols from previous submissions as well.
/// It is advised to avoid the use of this API within the compilers, as the compilers have additional
/// requirements for access checking that are not satisfied by this implementation, including the
/// avoidance of infinite recursion that could result from the use of the ISymbol APIs here, the detection
/// of use-site diagnostics, and additional returned details (from the compiler's internal APIs) that are
/// helpful for more precisely diagnosing reasons for accessibility failure.
///
public bool IsSymbolAccessibleWithin(
ISymbol symbol,
ISymbol within,
ITypeSymbol throughType = null)
{
if (symbol is null)
{
throw new ArgumentNullException(nameof(symbol));
}
if (within is null)
{
throw new ArgumentNullException(nameof(within));
}
if (!(within is INamedTypeSymbol || within is IAssemblySymbol))
{
throw new ArgumentException(string.Format(CodeAnalysisResources.IsSymbolAccessibleBadWithin, nameof(within)), nameof(within));
}
checkInCompilationReferences(symbol, nameof(symbol));
checkInCompilationReferences(within, nameof(within));
if (!(throughType is null))
{
checkInCompilationReferences(throughType, nameof(throughType));
}
return IsSymbolAccessibleWithinCore(symbol, within, throughType);
void checkInCompilationReferences(ISymbol s, string parameterName)
{
var containingAssembly = computeContainingAssembly(s);
if (!assemblyIsInReferences(containingAssembly))
{
throw new ArgumentException(string.Format(CodeAnalysisResources.IsSymbolAccessibleWrongAssembly, parameterName), parameterName);
}
}
bool assemblyIsInReferences(IAssemblySymbol a)
{
if (assemblyIsInCompilationReferences(a, this))
{
return true;
}
if (this.IsSubmission)
{
// Submissions can reference symbols from previous submissions and their referenced assemblies, even
// though those references are missing from this.References. We work around that by digging in
// to find references of previous submissions. See https://github.com/dotnet/roslyn/issues/27356
for (Compilation c = this.PreviousSubmission; c != null; c = c.PreviousSubmission)
{
if (assemblyIsInCompilationReferences(a, c))
{
return true;
}
}
}
return false;
}
bool assemblyIsInCompilationReferences(IAssemblySymbol a, Compilation compilation)
{
if (a.Equals(compilation.Assembly))
{
return true;
}
foreach (var reference in compilation.References)
{
if (a.Equals(compilation.GetAssemblyOrModuleSymbol(reference)))
{
return true;
}
}
return false;
}
IAssemblySymbol computeContainingAssembly(ISymbol s)
{
while (true)
{
switch (s.Kind)
{
case SymbolKind.Assembly:
return (IAssemblySymbol)s;
case SymbolKind.PointerType:
s = ((IPointerTypeSymbol)s).PointedAtType;
continue;
case SymbolKind.ArrayType:
s = ((IArrayTypeSymbol)s).ElementType;
continue;
case SymbolKind.Alias:
s = ((IAliasSymbol)s).Target;
continue;
case SymbolKind.Discard:
s = ((IDiscardSymbol)s).Type;
continue;
case SymbolKind.DynamicType:
case SymbolKind.ErrorType:
case SymbolKind.Preprocessing:
case SymbolKind.Namespace:
// these symbols are not restricted in where they can be accessed, so unless they report
// a containing assembly, we treat them as in the current assembly for access purposes
return s.ContainingAssembly ?? this.Assembly;
default:
return s.ContainingAssembly;
}
}
}
}
private protected abstract bool IsSymbolAccessibleWithinCore(
ISymbol symbol,
ISymbol within,
ITypeSymbol throughType);
internal abstract IConvertibleConversion ClassifyConvertibleConversion(IOperation source, ITypeSymbol destination, out Optional