// 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.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Instrumentation; using Microsoft.CodeAnalysis.Symbols; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { /// /// 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 sealed partial class CSharpCompilation : Compilation { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Changes to the public interface of this class should remain synchronized with the VB // version. Do not make any changes to the public interface without making the corresponding // change to the VB version. // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! internal static readonly ParallelOptions DefaultParallelOptions = new ParallelOptions(); private readonly CSharpCompilationOptions options; private readonly ImmutableArray syntaxTrees; // In ordinal order. private readonly ImmutableDictionary> rootNamespaces; private readonly DeclarationTable declarationTable; private readonly Lazy globalImports; private readonly Lazy globalNamespaceAlias; // alias symbol used to resolve "global::". private readonly Lazy scriptClass; private readonly CSharpCompilation previousSubmission; // All imports (using directives and extern aliases) in syntax trees in this compilation. // NOTE: We need to de-dup since the Imports objects that populate the list may be GC'd // and re-created. private ConcurrentSet lazyImportInfos; // Cache the CLS diagnostics for the whole compilation so they aren't computed repeatedly. // NOTE: Presently, we do not cache the per-tree diagnostics. private ImmutableArray lazyClsComplianceDiagnostics; private Conversions conversions; internal Conversions Conversions { get { if (conversions == null) { Interlocked.CompareExchange(ref conversions, new BuckStopsHereBinder(this).Conversions, null); } return conversions; } } /// /// Manages anonymous types declared in this compilation. Unifies types that are structurally equivalent. /// private AnonymousTypeManager anonymousTypeManager; private NamespaceSymbol lazyGlobalNamespace; internal readonly BuiltInOperators builtInOperators; /// /// The for this compilation. Do not access directly, use Assembly property /// instead. This field is lazily initialized by ReferenceManager, ReferenceManager.CacheLockObject must be locked /// while ReferenceManager "calculates" the value and assigns it, several threads must not perform duplicate /// "calculation" simultaneously. /// private SourceAssemblySymbol lazyAssemblySymbol; /// /// Holds onto data related to reference binding. /// The manager is shared among multiple compilations that we expect to have the same result of reference binding. /// In most cases this can be determined without performing the binding. If the compilation however contains a circular /// metadata reference (a metadata reference that refers back to the compilation) we need to avoid sharing of the binding results. /// We do so by creating a new reference manager for such compilation. /// private ReferenceManager referenceManager; /// /// Contains the main method of this assembly, if there is one. /// private EntryPoint lazyEntryPoint; /// /// The set of trees for which a has been added to the queue. /// private HashSet lazyCompilationUnitCompletedTrees; public override string Language { get { return LanguageNames.CSharp; } } public override bool IsCaseSensitive { get { return true; } } /// /// The options the compilation was created with. /// public new CSharpCompilationOptions Options { get { return options; } } internal AnonymousTypeManager AnonymousTypeManager { get { return anonymousTypeManager; } } internal override CommonAnonymousTypeManager CommonAnonymousTypeManager { get { return AnonymousTypeManager; } } /// /// The language version that was used to parse the syntax trees of this compilation. /// public LanguageVersion LanguageVersion { get; private set; } public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity) { return new ExtendedErrorTypeSymbol((NamespaceOrTypeSymbol)container, name, arity, null); } #region Constructors and Factories private static CSharpCompilationOptions DefaultOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); private static CSharpCompilationOptions DefaultSubmissionOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); /// /// Creates a new compilation from scratch. Methods such as AddSyntaxTrees or AddReferences /// on the returned object will allow to continue building up the Compilation incrementally. /// /// Simple assembly name. /// The syntax trees with the source code for the new compilation. /// The references for the new compilation. /// The compiler options to use. /// A new compilation. public static CSharpCompilation Create( string assemblyName, IEnumerable syntaxTrees = null, IEnumerable references = null, CSharpCompilationOptions options = null) { return Create( assemblyName, options ?? DefaultOptions, (syntaxTrees != null) ? syntaxTrees.Cast() : null, references, previousSubmission: null, returnType: null, hostObjectType: null, isSubmission: false); } /// /// Creates a new compilation that can be used in scripting. /// public static CSharpCompilation CreateSubmission( string assemblyName, SyntaxTree syntaxTree = null, IEnumerable references = null, CSharpCompilationOptions options = null, Compilation previousSubmission = null, Type returnType = null, Type hostObjectType = null) { CheckSubmissionOptions(options); return Create( assemblyName, options ?? DefaultSubmissionOptions, (syntaxTree != null) ? new[] { syntaxTree } : SpecializedCollections.EmptyEnumerable(), references, (CSharpCompilation)previousSubmission, returnType, hostObjectType, isSubmission: true); } private static CSharpCompilation Create( string assemblyName, CSharpCompilationOptions options, IEnumerable syntaxTrees, IEnumerable references, CSharpCompilation previousSubmission, Type returnType, Type hostObjectType, bool isSubmission) { Debug.Assert(options != null); CheckAssemblyName(assemblyName); var validatedReferences = ValidateReferences(references); ValidateSubmissionParameters(previousSubmission, returnType, ref hostObjectType); var compilation = new CSharpCompilation( assemblyName, options, validatedReferences, ImmutableArray.Empty, ImmutableDictionary.Create(ReferenceEqualityComparer.Instance), ImmutableDictionary.Create>(), DeclarationTable.Empty, previousSubmission, returnType, hostObjectType, isSubmission, referenceManager: null, reuseReferenceManager: false); if (syntaxTrees != null) { compilation = compilation.AddSyntaxTrees(syntaxTrees); } Debug.Assert((object)compilation.lazyAssemblySymbol == null); return compilation; } private CSharpCompilation( string assemblyName, CSharpCompilationOptions options, ImmutableArray references, ImmutableArray syntaxTrees, ImmutableDictionary syntaxTreeOrdinalMap, ImmutableDictionary> rootNamespaces, DeclarationTable declarationTable, CSharpCompilation previousSubmission, Type submissionReturnType, Type hostObjectType, bool isSubmission, ReferenceManager referenceManager, bool reuseReferenceManager, AsyncQueue eventQueue = null) : base(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap, eventQueue) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_Create, message: assemblyName)) { this.wellKnownMemberSignatureComparer = new WellKnownMembersSignatureComparer(this); this.options = options; this.syntaxTrees = syntaxTrees; this.rootNamespaces = rootNamespaces; this.declarationTable = declarationTable; Debug.Assert(syntaxTrees.All(tree => syntaxTrees[syntaxTreeOrdinalMap[tree]] == tree)); Debug.Assert(syntaxTrees.SetEquals(rootNamespaces.Keys.AsImmutable(), EqualityComparer.Default)); this.builtInOperators = new BuiltInOperators(this); this.scriptClass = new Lazy(BindScriptClass); this.globalImports = new Lazy(BindGlobalUsings); this.globalNamespaceAlias = new Lazy(CreateGlobalNamespaceAlias); this.anonymousTypeManager = new AnonymousTypeManager(this); this.LanguageVersion = CommonLanguageVersion(syntaxTrees); if (isSubmission) { Debug.Assert(previousSubmission == null || previousSubmission.HostObjectType == hostObjectType); this.previousSubmission = previousSubmission; } else { Debug.Assert(previousSubmission == null && submissionReturnType == null && hostObjectType == null); } if (reuseReferenceManager) { referenceManager.AssertCanReuseForCompilation(this); this.referenceManager = referenceManager; } else { this.referenceManager = new ReferenceManager( MakeSourceAssemblySimpleName(), options.AssemblyIdentityComparer, (referenceManager != null) ? referenceManager.ObservedMetadata : null); } Debug.Assert((object)this.lazyAssemblySymbol == null); if (EventQueue != null) EventQueue.Enqueue(new CompilationStartedEvent(this)); } } private static LanguageVersion CommonLanguageVersion(ImmutableArray syntaxTrees) { LanguageVersion? result = null; foreach (var tree in syntaxTrees) { var version = ((CSharpParseOptions)tree.Options).LanguageVersion; if (result == null) { result = version; } else if (result != version) { throw new ArgumentException("inconsistent language versions", nameof(syntaxTrees)); } } return result ?? CSharpParseOptions.Default.LanguageVersion; } /// /// Create a duplicate of this compilation with different symbol instances. /// public new CSharpCompilation Clone() { return new CSharpCompilation( this.AssemblyName, this.options, this.ExternalReferences, this.SyntaxTrees, this.syntaxTreeOrdinalMap, this.rootNamespaces, this.declarationTable, this.previousSubmission, this.SubmissionReturnType, this.HostObjectType, this.IsSubmission, this.referenceManager, reuseReferenceManager: true); } private CSharpCompilation UpdateSyntaxTrees( ImmutableArray syntaxTrees, ImmutableDictionary syntaxTreeOrdinalMap, ImmutableDictionary> rootNamespaces, DeclarationTable declarationTable, bool referenceDirectivesChanged) { return new CSharpCompilation( this.AssemblyName, this.options, this.ExternalReferences, syntaxTrees, syntaxTreeOrdinalMap, rootNamespaces, declarationTable, this.previousSubmission, this.SubmissionReturnType, this.HostObjectType, this.IsSubmission, this.referenceManager, reuseReferenceManager: !referenceDirectivesChanged); } /// /// Creates a new compilation with the specified name. /// public new CSharpCompilation WithAssemblyName(string assemblyName) { CheckAssemblyName(assemblyName); // Can't reuse references since the source assembly name changed and the referenced symbols might // have internals-visible-to relationship with this compilation or they might had a circular reference // to this compilation. return new CSharpCompilation( assemblyName, this.options, this.ExternalReferences, this.SyntaxTrees, this.syntaxTreeOrdinalMap, this.rootNamespaces, this.declarationTable, this.previousSubmission, this.SubmissionReturnType, this.HostObjectType, this.IsSubmission, this.referenceManager, reuseReferenceManager: assemblyName == this.AssemblyName); } /// /// Creates a new compilation with the specified references. /// /// /// The new will query the given for the underlying /// metadata as soon as the are needed. /// /// The new compilation uses whatever metadata is currently being provided by the . /// E.g. if the current compilation references a metadata file that has changed since the creation of the compilation /// the new compilation is going to use the updated version, while the current compilation will be using the previous (it doesn't change). /// public new CSharpCompilation WithReferences(IEnumerable references) { // References might have changed, don't reuse reference manager. // Don't even reuse observed metadata - let the manager query for the metadata again. return new CSharpCompilation( this.AssemblyName, this.options, ValidateReferences(references), this.SyntaxTrees, this.syntaxTreeOrdinalMap, this.rootNamespaces, this.declarationTable, this.previousSubmission, this.SubmissionReturnType, this.HostObjectType, this.IsSubmission, referenceManager: null, reuseReferenceManager: false); } /// /// Creates a new compilation with the specified references. /// public new CSharpCompilation WithReferences(params MetadataReference[] references) { return this.WithReferences((IEnumerable)references); } /// /// Creates a new compilation with the specified compilation options. /// public CSharpCompilation WithOptions(CSharpCompilationOptions options) { // Checks to see if the new options support reusing the reference manager bool reuseReferenceManager = this.Options.CanReuseCompilationReferenceManager(options); return new CSharpCompilation( this.AssemblyName, options, this.ExternalReferences, this.syntaxTrees, this.syntaxTreeOrdinalMap, this.rootNamespaces, this.declarationTable, this.previousSubmission, this.SubmissionReturnType, this.HostObjectType, this.IsSubmission, this.referenceManager, reuseReferenceManager); } /// /// Returns a new compilation with the given compilation set as the previous submission. /// internal CSharpCompilation WithPreviousSubmission(CSharpCompilation newPreviousSubmission) { if (!this.IsSubmission) { throw new NotSupportedException("Can't have a previousSubmission when not a submission"); } // Reference binding doesn't depend on previous submission so we can reuse it. return new CSharpCompilation( this.AssemblyName, this.options, this.ExternalReferences, this.SyntaxTrees, this.syntaxTreeOrdinalMap, this.rootNamespaces, this.declarationTable, newPreviousSubmission, this.SubmissionReturnType, this.HostObjectType, this.IsSubmission, this.referenceManager, reuseReferenceManager: true); } /// /// Returns a new compilation with a given event queue. /// internal override Compilation WithEventQueue(AsyncQueue eventQueue) { return new CSharpCompilation( this.AssemblyName, this.options, this.ExternalReferences, this.SyntaxTrees, this.syntaxTreeOrdinalMap, this.rootNamespaces, this.declarationTable, this.previousSubmission, this.SubmissionReturnType, this.HostObjectType, this.IsSubmission, this.referenceManager, reuseReferenceManager: true, eventQueue: eventQueue); } #endregion #region Submission internal new CSharpCompilation PreviousSubmission { get { return previousSubmission; } } // TODO (tomat): consider moving this method to SemanticModel /// /// Returns the type of the submission return value. /// /// /// The type of the last expression of the submission. /// Null if the type of the last expression is unknown (null). /// Void type if the type of the last expression statement is void or /// the submission ends with a declaration or statement that is not an expression statement. /// /// /// Note that the return type is System.Void for both compilations "System.Console.WriteLine();" and "System.Console.WriteLine()", /// and is False for the former and True for the latter. /// /// True if the submission has value, i.e. if it ends with a statement that is an expression statement. /// The compilation doesn't represent a submission ( return false). internal new TypeSymbol GetSubmissionResultType(out bool hasValue) { if (!IsSubmission) { throw new InvalidOperationException(CSharpResources.ThisCompilationNotInteractive); } hasValue = false; // submission can be empty or comprise of a script file SyntaxTree tree = SyntaxTrees.SingleOrDefault(); if (tree == null || tree.Options.Kind != SourceCodeKind.Interactive) { return GetSpecialType(SpecialType.System_Void); } var lastStatement = (GlobalStatementSyntax)tree.GetCompilationUnitRoot().Members.LastOrDefault(decl => decl.Kind() == SyntaxKind.GlobalStatement); if (lastStatement == null || lastStatement.Statement.Kind() != SyntaxKind.ExpressionStatement) { return GetSpecialType(SpecialType.System_Void); } var expressionStatement = (ExpressionStatementSyntax)lastStatement.Statement; if (!expressionStatement.SemicolonToken.IsMissing) { return GetSpecialType(SpecialType.System_Void); } var model = GetSemanticModel(tree); hasValue = true; var expression = expressionStatement.Expression; var info = model.GetTypeInfo(expression); return (TypeSymbol)info.ConvertedType; } #endregion #region Syntax Trees (maintain an ordered list) /// /// The syntax trees (parsed from source code) that this compilation was created with. /// public new ImmutableArray SyntaxTrees { get { return this.syntaxTrees; } } /// /// Returns true if this compilation contains the specified tree. False otherwise. /// public new bool ContainsSyntaxTree(SyntaxTree syntaxTree) { var cstree = syntaxTree as SyntaxTree; return cstree != null && rootNamespaces.ContainsKey((cstree)); } /// /// Creates a new compilation with additional syntax trees. /// public new CSharpCompilation AddSyntaxTrees(params SyntaxTree[] trees) { return AddSyntaxTrees((IEnumerable)trees); } /// /// Creates a new compilation with additional syntax trees. /// public new CSharpCompilation AddSyntaxTrees(IEnumerable trees) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_AddSyntaxTrees, message: this.AssemblyName)) { if (trees == null) { throw new ArgumentNullException("trees"); } if (trees.IsEmpty()) { return this; } // We're using a try-finally for this builder because there's a test that // specifically checks for one or more of the argument exceptions below // and we don't want to see console spew (even though we don't generally // care about pool "leaks" in exceptional cases). Alternatively, we // could create a new ArrayBuilder. var builder = ArrayBuilder.GetInstance(); try { builder.AddRange(this.SyntaxTrees); bool referenceDirectivesChanged = false; var oldTreeCount = this.SyntaxTrees.Length; var ordinalMap = this.syntaxTreeOrdinalMap; var declMap = rootNamespaces; var declTable = declarationTable; int i = 0; foreach (var tree in trees.Cast()) { if (tree == null) { throw new ArgumentNullException("trees[" + i + "]"); } if (!tree.HasCompilationUnitRoot) { throw new ArgumentException(String.Format(CSharpResources.TreeMustHaveARootNodeWith, i)); } if (declMap.ContainsKey(tree)) { throw new ArgumentException(CSharpResources.SyntaxTreeAlreadyPresent, String.Format(CSharpResources.Trees0, i)); } if (IsSubmission && tree.Options.Kind == SourceCodeKind.Regular) { throw new ArgumentException(CSharpResources.SubmissionCanOnlyInclude, String.Format(CSharpResources.Trees0, i)); } AddSyntaxTreeToDeclarationMapAndTable(tree, options, IsSubmission, ref declMap, ref declTable, ref referenceDirectivesChanged); builder.Add(tree); ordinalMap = ordinalMap.Add(tree, oldTreeCount + i); i++; } if (IsSubmission && declMap.Count > 1) { throw new ArgumentException(CSharpResources.SubmissionCanHaveAtMostOne, "trees"); } return UpdateSyntaxTrees(builder.ToImmutable(), ordinalMap, declMap, declTable, referenceDirectivesChanged); } finally { builder.Free(); } } } private static void AddSyntaxTreeToDeclarationMapAndTable( SyntaxTree tree, CSharpCompilationOptions options, bool isSubmission, ref ImmutableDictionary> declMap, ref DeclarationTable declTable, ref bool referenceDirectivesChanged) { var lazyRoot = new Lazy(() => DeclarationTreeBuilder.ForTree(tree, options.ScriptClassName ?? "", isSubmission)); declMap = declMap.SetItem(tree, lazyRoot); declTable = declTable.AddRootDeclaration(lazyRoot); referenceDirectivesChanged = referenceDirectivesChanged || tree.HasReferenceDirectives(); } /// /// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees /// added later. /// public new CSharpCompilation RemoveSyntaxTrees(params SyntaxTree[] trees) { return RemoveSyntaxTrees((IEnumerable)trees); } /// /// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees /// added later. /// public new CSharpCompilation RemoveSyntaxTrees(IEnumerable trees) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_RemoveSyntaxTrees, message: this.AssemblyName)) { if (trees == null) { throw new ArgumentNullException("trees"); } if (trees.IsEmpty()) { return this; } bool referenceDirectivesChanged = false; var removeSet = new HashSet(); var declMap = rootNamespaces; var declTable = declarationTable; foreach (var tree in trees.Cast()) { RemoveSyntaxTreeFromDeclarationMapAndTable(tree, ref declMap, ref declTable, ref referenceDirectivesChanged); removeSet.Add(tree); } Debug.Assert(!removeSet.IsEmpty()); // We're going to have to revise the ordinals of all // trees after the first one removed, so just build // a new map. var ordinalMap = ImmutableDictionary.Create(); var builder = ArrayBuilder.GetInstance(); int i = 0; foreach (var tree in this.SyntaxTrees) { if (!removeSet.Contains(tree)) { builder.Add(tree); ordinalMap = ordinalMap.Add(tree, i++); } } return UpdateSyntaxTrees(builder.ToImmutableAndFree(), ordinalMap, declMap, declTable, referenceDirectivesChanged); } } private static void RemoveSyntaxTreeFromDeclarationMapAndTable( SyntaxTree tree, ref ImmutableDictionary> declMap, ref DeclarationTable declTable, ref bool referenceDirectivesChanged) { Lazy lazyRoot; if (!declMap.TryGetValue(tree, out lazyRoot)) { throw new ArgumentException(string.Format(CSharpResources.SyntaxTreeNotFoundTo, tree), "trees"); } declTable = declTable.RemoveRootDeclaration(lazyRoot); declMap = declMap.Remove(tree); referenceDirectivesChanged = referenceDirectivesChanged || tree.HasReferenceDirectives(); } /// /// Creates a new compilation without any syntax trees. Preserves metadata info /// from this compilation for use with trees added later. /// public new CSharpCompilation RemoveAllSyntaxTrees() { return UpdateSyntaxTrees( ImmutableArray.Empty, ImmutableDictionary.Create(), ImmutableDictionary.Create>(), DeclarationTable.Empty, referenceDirectivesChanged: declarationTable.ReferenceDirectives.Any()); } /// /// Creates a new compilation without the old tree but with the new tree. /// public new CSharpCompilation ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_ReplaceSyntaxTree, message: this.AssemblyName)) { // this is just to force a cast exception oldTree = (CSharpSyntaxTree)oldTree; newTree = (CSharpSyntaxTree)newTree; if (oldTree == null) { throw new ArgumentNullException("oldTree"); } if (newTree == null) { return this.RemoveSyntaxTrees(oldTree); } else if (newTree == oldTree) { return this; } if (!newTree.HasCompilationUnitRoot) { throw new ArgumentException(CSharpResources.TreeMustHaveARootNodeWith, "newTree"); } var declMap = rootNamespaces; var declTable = declarationTable; bool referenceDirectivesChanged = false; // TODO(tomat): Consider comparing #r's of the old and the new tree. If they are exactly the same we could still reuse. // This could be a perf win when editing a script file in the IDE. The services create a new compilation every keystroke // that replaces the tree with a new one. RemoveSyntaxTreeFromDeclarationMapAndTable(oldTree, ref declMap, ref declTable, ref referenceDirectivesChanged); AddSyntaxTreeToDeclarationMapAndTable(newTree, options, this.IsSubmission, ref declMap, ref declTable, ref referenceDirectivesChanged); var ordinalMap = this.syntaxTreeOrdinalMap; Debug.Assert(ordinalMap.ContainsKey(oldTree)); // Checked by RemoveSyntaxTreeFromDeclarationMapAndTable var oldOrdinal = ordinalMap[oldTree]; var newArray = this.SyntaxTrees.SetItem(oldOrdinal, newTree); // CONSIDER: should this be an operation on ImmutableDictionary? ordinalMap = ordinalMap.Remove(oldTree); ordinalMap = ordinalMap.SetItem(newTree, oldOrdinal); return UpdateSyntaxTrees(newArray, ordinalMap, declMap, declTable, referenceDirectivesChanged); } } #endregion #region References internal override CommonReferenceManager CommonGetBoundReferenceManager() { return GetBoundReferenceManager(); } internal new ReferenceManager GetBoundReferenceManager() { if ((object)lazyAssemblySymbol == null) { referenceManager.CreateSourceAssemblyForCompilation(this); Debug.Assert((object)lazyAssemblySymbol != null); } // referenceManager can only be accessed after we initialized the lazyAssemblySymbol. // In fact, initialization of the assembly symbol might change the reference manager. return referenceManager; } // for testing only: internal bool ReferenceManagerEquals(CSharpCompilation other) { return ReferenceEquals(this.referenceManager, other.referenceManager); } public override ImmutableArray DirectiveReferences { get { return GetBoundReferenceManager().DirectiveReferences; } } internal override IDictionary ReferenceDirectiveMap { get { return GetBoundReferenceManager().ReferenceDirectiveMap; } } // for testing purposes internal IEnumerable ExternAliases { get { return GetBoundReferenceManager().ExternAliases; } } /// /// Gets the or for a metadata reference used to create this compilation. /// /// or corresponding to the given reference or null if there is none. /// /// Uses object identity when comparing two references. /// internal new Symbol GetAssemblyOrModuleSymbol(MetadataReference reference) { if (reference == null) { throw new ArgumentNullException("reference"); } if (reference.Properties.Kind == MetadataImageKind.Assembly) { return GetBoundReferenceManager().GetReferencedAssemblySymbol(reference); } else { Debug.Assert(reference.Properties.Kind == MetadataImageKind.Module); int index = GetBoundReferenceManager().GetReferencedModuleIndex(reference); return index < 0 ? null : this.Assembly.Modules[index]; } } public override IEnumerable ReferencedAssemblyNames { get { return Assembly.Modules.SelectMany(module => module.GetReferencedAssemblies()); } } /// /// All reference directives used in this compilation. /// internal override IEnumerable ReferenceDirectives { get { return declarationTable.ReferenceDirectives; } } /// /// Returns a metadata reference that a given #r resolves to. /// /// #r directive. /// Metadata reference the specified directive resolves to. public MetadataReference GetDirectiveReference(ReferenceDirectiveTriviaSyntax directive) { return ReferenceDirectiveMap[directive.File.ValueText]; } /// /// Creates a new compilation with additional metadata references. /// public new CSharpCompilation AddReferences(params MetadataReference[] references) { return (CSharpCompilation)base.AddReferences(references); } /// /// Creates a new compilation with additional metadata references. /// public new CSharpCompilation AddReferences(IEnumerable references) { return (CSharpCompilation)base.AddReferences(references); } /// /// Creates a new compilation without the specified metadata references. /// public new CSharpCompilation RemoveReferences(params MetadataReference[] references) { return (CSharpCompilation)base.RemoveReferences(references); } /// /// Creates a new compilation without the specified metadata references. /// public new CSharpCompilation RemoveReferences(IEnumerable references) { return (CSharpCompilation)base.RemoveReferences(references); } /// /// Creates a new compilation without any metadata references /// public new CSharpCompilation RemoveAllReferences() { return (CSharpCompilation)base.RemoveAllReferences(); } /// /// Creates a new compilation with an old metadata reference replaced with a new metadata reference. /// public new CSharpCompilation ReplaceReference(MetadataReference oldReference, MetadataReference newReference) { return (CSharpCompilation)base.ReplaceReference(oldReference, newReference); } public override CompilationReference ToMetadataReference(ImmutableArray aliases = default(ImmutableArray), bool embedInteropTypes = false) { return new CSharpCompilationReference(this, aliases, embedInteropTypes); } // Get all modules in this compilation, including the source module, added modules, and all // modules of referenced assemblies that do not come from an assembly with an extern alias. // Metadata imported from aliased assemblies is not visible at the source level except through // the use of an extern alias directive. So exclude them from this list which is used to construct // the global namespace. private IEnumerable GetAllUnaliasedModules() { // Get all assemblies in this compilation, including the source assembly and all referenced assemblies. ArrayBuilder modules = new ArrayBuilder(); // NOTE: This includes referenced modules - they count as modules of the compilation assembly. modules.AddRange(this.Assembly.Modules); foreach (var pair in GetBoundReferenceManager().ReferencedAssembliesMap) { MetadataReference reference = pair.Key; ReferenceManager.ReferencedAssembly referencedAssembly = pair.Value; if (reference.Properties.Kind == MetadataImageKind.Assembly) // Already handled modules above. { if (referencedAssembly.DeclarationsAccessibleWithoutAlias()) { modules.AddRange(referencedAssembly.Symbol.Modules); } } } return modules; } /// /// Gets the that corresponds to the assembly symbol. /// public new MetadataReference GetMetadataReference(IAssemblySymbol assemblySymbol) { return this.GetBoundReferenceManager().ReferencedAssembliesMap.Where(kvp => object.ReferenceEquals(kvp.Value.Symbol, assemblySymbol)).Select(kvp => kvp.Key).FirstOrDefault(); } #endregion #region Symbols /// /// The AssemblySymbol that represents the assembly being created. /// internal SourceAssemblySymbol SourceAssembly { get { GetBoundReferenceManager(); return lazyAssemblySymbol; } } /// /// The AssemblySymbol that represents the assembly being created. /// internal new AssemblySymbol Assembly { get { return SourceAssembly; } } /// /// Get a ModuleSymbol that refers to the module being created by compiling all of the code. /// By getting the GlobalNamespace property of that module, all of the namespaces and types /// defined in source code can be obtained. /// internal new ModuleSymbol SourceModule { get { return Assembly.Modules[0]; } } /// /// Gets the root namespace that contains all namespaces and types defined in source code or in /// referenced metadata, merged into a single namespace hierarchy. /// internal new NamespaceSymbol GlobalNamespace { get { if ((object)lazyGlobalNamespace == null) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_GetGlobalNamespace, message: this.AssemblyName)) { // Get the root namespace from each module, and merge them all together HashSet allGlobalNamespaces = new HashSet(); foreach (ModuleSymbol module in GetAllUnaliasedModules()) { allGlobalNamespaces.Add(module.GlobalNamespace); } var result = MergedNamespaceSymbol.Create(new NamespaceExtent(this), null, allGlobalNamespaces.AsImmutable()); Interlocked.CompareExchange(ref lazyGlobalNamespace, result, null); } } return lazyGlobalNamespace; } } /// /// Given for the specified module or assembly namespace, gets the corresponding compilation /// namespace (merged namespace representation for all namespace declarations and references /// with contributions for the namespaceSymbol). Can return null if no corresponding /// namespace can be bound in this compilation with the same name. /// internal new NamespaceSymbol GetCompilationNamespace(INamespaceSymbol namespaceSymbol) { if (namespaceSymbol is NamespaceSymbol && namespaceSymbol.NamespaceKind == NamespaceKind.Compilation && namespaceSymbol.ContainingCompilation == this) { return (NamespaceSymbol)namespaceSymbol; } var containingNamespace = namespaceSymbol.ContainingNamespace; if (containingNamespace == null) { return this.GlobalNamespace; } var current = GetCompilationNamespace(containingNamespace); if ((object)current != null) { return current.GetNestedNamespace(namespaceSymbol.Name); } return null; } private ConcurrentDictionary externAliasTargets; internal bool GetExternAliasTarget(string aliasName, out NamespaceSymbol @namespace) { if (externAliasTargets == null) { Interlocked.CompareExchange(ref this.externAliasTargets, new ConcurrentDictionary(), null); } else if (externAliasTargets.TryGetValue(aliasName, out @namespace)) { return !(@namespace is MissingNamespaceSymbol); } ArrayBuilder builder = null; foreach (var referencedAssembly in GetBoundReferenceManager().ReferencedAssembliesMap.Values) { if (referencedAssembly.Aliases.Contains(aliasName)) { builder = builder ?? ArrayBuilder.GetInstance(); builder.Add(referencedAssembly.Symbol.GlobalNamespace); } } bool foundNamespace = builder != null; // We want to cache failures as well as successes so that subsequent incorrect extern aliases with the // same alias will have the same target. @namespace = foundNamespace ? MergedNamespaceSymbol.Create(new NamespaceExtent(this), namespacesToMerge: builder.ToImmutableAndFree(), containingNamespace: null, nameOpt: null) : new MissingNamespaceSymbol(new MissingModuleSymbol(new MissingAssemblySymbol(new AssemblyIdentity(System.Guid.NewGuid().ToString())), ordinal: -1)); // Use GetOrAdd in case another thread beat us to the punch (i.e. should return the same object for the same alias, every time). @namespace = externAliasTargets.GetOrAdd(aliasName, @namespace); Debug.Assert(foundNamespace == !(@namespace is MissingNamespaceSymbol)); return foundNamespace; } /// /// A symbol representing the implicit Script class. This is null if the class is not /// defined in the compilation. /// internal new NamedTypeSymbol ScriptClass { get { return scriptClass.Value; } } /// /// 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. private ImplicitNamedTypeSymbol BindScriptClass() { if (options.ScriptClassName == null || !options.ScriptClassName.IsValidClrTypeName()) { return null; } var namespaceOrType = this.Assembly.GlobalNamespace.GetNamespaceOrTypeByQualifiedName(options.ScriptClassName.Split('.')).AsSingleton(); return namespaceOrType as ImplicitNamedTypeSymbol; } internal Imports GlobalImports { get { return globalImports.Value; } } internal IEnumerable GlobalUsings { get { return GlobalImports.Usings.Select(u => u.NamespaceOrType); } } internal AliasSymbol GlobalNamespaceAlias { get { return globalNamespaceAlias.Value; } } /// /// Get the symbol for the predefined type from the COR Library referenced by this compilation. /// internal new NamedTypeSymbol GetSpecialType(SpecialType specialType) { if (specialType <= SpecialType.None || specialType > SpecialType.Count) { throw new ArgumentOutOfRangeException("specialType"); } var result = Assembly.GetSpecialType(specialType); Debug.Assert(result.SpecialType == specialType); return result; } /// /// Get the symbol for the predefined type member from the COR Library referenced by this compilation. /// internal Symbol GetSpecialTypeMember(SpecialMember specialMember) { return Assembly.GetSpecialTypeMember(specialMember); } internal TypeSymbol GetTypeByReflectionType(Type type, DiagnosticBag diagnostics) { var result = Assembly.GetTypeByReflectionType(type, includeReferences: true); if ((object)result == null) { var errorType = new ExtendedErrorTypeSymbol(this, type.Name, 0, CreateReflectionTypeNotFoundError(type)); diagnostics.Add(errorType.ErrorInfo, NoLocation.Singleton); result = errorType; } return result; } private static CSDiagnosticInfo CreateReflectionTypeNotFoundError(Type type) { // The type or namespace name '{0}' could not be found in the global namespace (are you missing an assembly reference?) return new CSDiagnosticInfo( ErrorCode.ERR_GlobalSingleTypeNameNotFound, new object[] { type.AssemblyQualifiedName }, ImmutableArray.Empty, ImmutableArray.Empty ); } // The type of host object model if available. private TypeSymbol lazyHostObjectTypeSymbol; internal TypeSymbol GetHostObjectTypeSymbol() { if (HostObjectType != null && (object)lazyHostObjectTypeSymbol == null) { TypeSymbol symbol = Assembly.GetTypeByReflectionType(HostObjectType, includeReferences: true); if ((object)symbol == null) { MetadataTypeName mdName = MetadataTypeName.FromNamespaceAndTypeName(HostObjectType.Namespace ?? String.Empty, HostObjectType.Name, useCLSCompliantNameArityEncoding: true); symbol = new MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo( new MissingAssemblySymbol(AssemblyIdentity.FromAssemblyDefinition(HostObjectType.GetTypeInfo().Assembly)).Modules[0], ref mdName, CreateReflectionTypeNotFoundError(HostObjectType), SpecialType.None); } Interlocked.CompareExchange(ref lazyHostObjectTypeSymbol, symbol, null); } return lazyHostObjectTypeSymbol; } internal TypeSymbol GetSubmissionReturnType() { if (IsSubmission && (object)ScriptClass != null) { // the second parameter of Script class instance constructor is the submission return value: return ((MethodSymbol)ScriptClass.GetMembers(WellKnownMemberNames.InstanceConstructorName)[0]).Parameters[1].Type; } else { return null; } } /// /// 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. /// internal new NamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName) { return this.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName, includeReferences: true, isWellKnownType: false); } /// /// The TypeSymbol for the type 'dynamic' in this Compilation. /// internal new TypeSymbol DynamicType { get { return AssemblySymbol.DynamicType; } } /// /// The NamedTypeSymbol for the .NET System.Object type, which could have a TypeKind of /// Error if there was no COR Library in this Compilation. /// internal new NamedTypeSymbol ObjectType { get { return this.Assembly.ObjectType; } } internal bool DeclaresTheObjectClass { get { return SourceAssembly.DeclaresTheObjectClass; } } internal new MethodSymbol GetEntryPoint(CancellationToken cancellationToken) { EntryPoint entryPoint = GetEntryPointAndDiagnostics(cancellationToken); return entryPoint == null ? null : entryPoint.MethodSymbol; } internal EntryPoint GetEntryPointAndDiagnostics(CancellationToken cancellationToken) { if (!this.Options.OutputKind.IsApplication()) { return null; } Debug.Assert(!this.IsSubmission); if (this.Options.MainTypeName != null && !this.Options.MainTypeName.IsValidClrTypeName()) { Debug.Assert(!this.Options.Errors.IsDefaultOrEmpty); return new EntryPoint(null, ImmutableArray.Empty); } if (this.lazyEntryPoint == null) { MethodSymbol entryPoint; ImmutableArray diagnostics; FindEntryPoint(cancellationToken, out entryPoint, out diagnostics); Interlocked.CompareExchange(ref this.lazyEntryPoint, new EntryPoint(entryPoint, diagnostics), null); } return this.lazyEntryPoint; } private void FindEntryPoint(CancellationToken cancellationToken, out MethodSymbol entryPoint, out ImmutableArray sealedDiagnostics) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_FindEntryPoint, message: this.AssemblyName, cancellationToken: cancellationToken)) { DiagnosticBag diagnostics = DiagnosticBag.GetInstance(); try { entryPoint = null; ArrayBuilder entryPointCandidates; NamedTypeSymbol mainType; string mainTypeName = this.Options.MainTypeName; NamespaceSymbol globalNamespace = this.SourceModule.GlobalNamespace; if (mainTypeName != null) { // Global code is the entry point, ignore all other Mains. // TODO: don't special case scripts (DevDiv #13119). if ((object)this.ScriptClass != null) { // CONSIDER: we could use the symbol instead of just the name. diagnostics.Add(ErrorCode.WRN_MainIgnored, NoLocation.Singleton, mainTypeName); return; } var mainTypeOrNamespace = globalNamespace.GetNamespaceOrTypeByQualifiedName(mainTypeName.Split('.')).OfMinimalArity(); if ((object)mainTypeOrNamespace == null) { diagnostics.Add(ErrorCode.ERR_MainClassNotFound, NoLocation.Singleton, mainTypeName); return; } mainType = mainTypeOrNamespace as NamedTypeSymbol; if ((object)mainType == null || mainType.IsGenericType || (mainType.TypeKind != TypeKind.Class && mainType.TypeKind != TypeKind.Struct)) { diagnostics.Add(ErrorCode.ERR_MainClassNotClass, mainTypeOrNamespace.Locations.First(), mainTypeOrNamespace); return; } entryPointCandidates = ArrayBuilder.GetInstance(); EntryPointCandidateFinder.FindCandidatesInSingleType(mainType, entryPointCandidates, cancellationToken); // NOTE: Any return after this point must free entryPointCandidates. } else { mainType = null; entryPointCandidates = ArrayBuilder.GetInstance(); EntryPointCandidateFinder.FindCandidatesInNamespace(globalNamespace, entryPointCandidates, cancellationToken); // NOTE: Any return after this point must free entryPointCandidates. // global code is the entry point, ignore all other Mains: if ((object)this.ScriptClass != null) { foreach (var main in entryPointCandidates) { diagnostics.Add(ErrorCode.WRN_MainIgnored, main.Locations.First(), main); } entryPointCandidates.Free(); return; } } DiagnosticBag warnings = DiagnosticBag.GetInstance(); var viableEntryPoints = ArrayBuilder.GetInstance(); foreach (var candidate in entryPointCandidates) { if (!candidate.HasEntryPointSignature()) { // a single error for partial methods: warnings.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate); continue; } if (candidate.IsGenericMethod || candidate.ContainingType.IsGenericType) { // a single error for partial methods: warnings.Add(ErrorCode.WRN_MainCantBeGeneric, candidate.Locations.First(), candidate); continue; } if (candidate.IsAsync) { diagnostics.Add(ErrorCode.ERR_MainCantBeAsync, candidate.Locations.First(), candidate); } viableEntryPoints.Add(candidate); } if ((object)mainType == null || viableEntryPoints.Count == 0) { diagnostics.AddRange(warnings); } warnings.Free(); if (viableEntryPoints.Count == 0) { if ((object)mainType == null) { diagnostics.Add(ErrorCode.ERR_NoEntryPoint, NoLocation.Singleton); } else { diagnostics.Add(ErrorCode.ERR_NoMainInClass, mainType.Locations.First(), mainType); } } else if (viableEntryPoints.Count > 1) { viableEntryPoints.Sort(LexicalOrderSymbolComparer.Instance); var info = new CSDiagnosticInfo( ErrorCode.ERR_MultipleEntryPoints, args: SpecializedCollections.EmptyArray(), symbols: viableEntryPoints.OfType().AsImmutable(), additionalLocations: viableEntryPoints.Select(m => m.Locations.First()).OfType().AsImmutable()); diagnostics.Add(new CSDiagnostic(info, viableEntryPoints.First().Locations.First())); } else { entryPoint = viableEntryPoints[0]; } viableEntryPoints.Free(); entryPointCandidates.Free(); } finally { sealedDiagnostics = diagnostics.ToReadOnlyAndFree(); } } } internal class EntryPoint { public readonly MethodSymbol MethodSymbol; public readonly ImmutableArray Diagnostics; public EntryPoint(MethodSymbol methodSymbol, ImmutableArray diagnostics) { this.MethodSymbol = methodSymbol; this.Diagnostics = diagnostics; } } internal bool MightContainNoPiaLocalTypes() { return SourceAssembly.MightContainNoPiaLocalTypes(); } // NOTE(cyrusn): There is a bit of a discoverability problem with this method and the same // named method in SyntaxTreeSemanticModel. Technically, i believe these are the appropriate // locations for these methods. This method has no dependencies on anything but the // compilation, while the other method needs a bindings object to determine what bound node // an expression syntax binds to. Perhaps when we document these methods we should explain // where a user can find the other. public Conversion ClassifyConversion(ITypeSymbol source, ITypeSymbol destination) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_ClassifyConversion, message: this.AssemblyName)) { // Note that it is possible for there to be both an implicit user-defined conversion // and an explicit built-in conversion from source to destination. In that scenario // this method returns the implicit conversion. if ((object)source == null) { throw new ArgumentNullException("source"); } if ((object)destination == null) { throw new ArgumentNullException("destination"); } var cssource = source.EnsureCSharpSymbolOrNull("source"); var csdest = destination.EnsureCSharpSymbolOrNull("destination"); HashSet useSiteDiagnostics = null; return Conversions.ClassifyConversion(cssource, csdest, ref useSiteDiagnostics); } } /// /// Returns a new ArrayTypeSymbol representing an array type tied to the base types of the /// COR Library in this Compilation. /// internal ArrayTypeSymbol CreateArrayTypeSymbol(TypeSymbol elementType, int rank = 1) { if ((object)elementType == null) { throw new ArgumentNullException("elementType"); } return new ArrayTypeSymbol(this.Assembly, elementType, ImmutableArray.Empty, rank); } /// /// Returns a new PointerTypeSymbol representing a pointer type tied to a type in this Compilation. /// internal PointerTypeSymbol CreatePointerTypeSymbol(TypeSymbol elementType) { if ((object)elementType == null) { throw new ArgumentNullException("elementType"); } return new PointerTypeSymbol(elementType); } #endregion #region Binding /// /// Gets a new SyntaxTreeSemanticModel for the specified syntax tree. /// public new SemanticModel GetSemanticModel(SyntaxTree syntaxTree) { if (syntaxTree == null) { throw new ArgumentNullException("tree"); } if (!this.SyntaxTrees.Contains((SyntaxTree)syntaxTree)) { throw new ArgumentException("tree"); } return new SyntaxTreeSemanticModel(this, (SyntaxTree)syntaxTree); } // When building symbols from the declaration table (lazily), or inside a type, or when // compiling a method body, we may not have a BinderContext in hand for the enclosing // scopes. Therefore, we build them when needed (and cache them) using a ContextBuilder. // Since a ContextBuilder is only a cache, and the identity of the ContextBuilders and // BinderContexts have no semantic meaning, we can reuse them or rebuild them, whichever is // most convenient. We store them using weak references so that GC pressure will cause them // to be recycled. private WeakReference[] binderFactories; internal BinderFactory GetBinderFactory(SyntaxTree syntaxTree) { var treeNum = GetSyntaxTreeOrdinal(syntaxTree); var binderFactories = this.binderFactories; if (binderFactories == null) { binderFactories = new WeakReference[this.syntaxTrees.Length]; binderFactories = Interlocked.CompareExchange(ref this.binderFactories, binderFactories, null) ?? binderFactories; } BinderFactory previousFactory; var previousWeakReference = binderFactories[treeNum]; if (previousWeakReference != null && previousWeakReference.TryGetTarget(out previousFactory)) { return previousFactory; } return AddNewFactory(syntaxTree, ref binderFactories[treeNum]); } private BinderFactory AddNewFactory(SyntaxTree syntaxTree, ref WeakReference slot) { var newFactory = new BinderFactory(this, syntaxTree); var newWeakReference = new WeakReference(newFactory); while (true) { BinderFactory previousFactory; WeakReference previousWeakReference = slot; if (previousWeakReference != null && previousWeakReference.TryGetTarget(out previousFactory)) { return previousFactory; } if (Interlocked.CompareExchange(ref slot, newWeakReference, previousWeakReference) == previousWeakReference) { return newFactory; } } } internal Binder GetBinder(SyntaxReference reference) { return GetBinderFactory(reference.SyntaxTree).GetBinder((CSharpSyntaxNode)reference.GetSyntax()); } internal Binder GetBinder(CSharpSyntaxNode syntax) { return GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax); } /// /// Returns imported symbols for the given declaration. /// internal Imports GetImports(SingleNamespaceDeclaration declaration) { return GetBinderFactory(declaration.SyntaxReference.SyntaxTree).GetImportsBinder((CSharpSyntaxNode)declaration.SyntaxReference.GetSyntax()).GetImports(); } internal Imports GetSubmissionImports() { return ((SourceNamespaceSymbol)SourceModule.GlobalNamespace).GetBoundImportsMerged().SingleOrDefault() ?? Imports.Empty; } internal InteractiveUsingsBinder GetInteractiveUsingsBinder() { Debug.Assert(IsSubmission); // empty compilation: if ((object)ScriptClass == null) { Debug.Assert(SyntaxTrees.Length == 0); return null; } return GetBinderFactory(SyntaxTrees.Single()).GetInteractiveUsingsBinder(); } private Imports BindGlobalUsings() { return Imports.FromGlobalUsings(this); } private AliasSymbol CreateGlobalNamespaceAlias() { return AliasSymbol.CreateGlobalNamespaceAlias(this.GlobalNamespace, new InContainerBinder(this.GlobalNamespace, new BuckStopsHereBinder(this))); } void CompleteTree(SyntaxTree tree) { bool completedCompilationUnit = false; bool completedCompilation = false; if (lazyCompilationUnitCompletedTrees == null) Interlocked.CompareExchange(ref lazyCompilationUnitCompletedTrees, new HashSet(), null); lock (lazyCompilationUnitCompletedTrees) { if (lazyCompilationUnitCompletedTrees.Add(tree)) { completedCompilationUnit = true; if (lazyCompilationUnitCompletedTrees.Count == SyntaxTrees.Length) { completedCompilation = true; } } } if (completedCompilationUnit) { EventQueue.Enqueue(new CompilationUnitCompletedEvent(this, tree)); } if (completedCompilation) { EventQueue.Enqueue(new CompilationCompletedEvent(this)); EventQueue.Complete(); // signal the end of compilation events } } internal void ReportUnusedImports(DiagnosticBag diagnostics, CancellationToken cancellationToken, SyntaxTree filterTree = null) { if (this.lazyImportInfos != null) { foreach (ImportInfo info in this.lazyImportInfos) { cancellationToken.ThrowIfCancellationRequested(); SyntaxTree infoTree = info.Tree; if (filterTree == null || filterTree == infoTree) { TextSpan infoSpan = info.Span; if (!this.IsImportDirectiveUsed(infoTree, infoSpan.Start)) { ErrorCode code = info.Kind == SyntaxKind.ExternAliasDirective ? ErrorCode.HDN_UnusedExternAlias : ErrorCode.HDN_UnusedUsingDirective; diagnostics.Add(code, infoTree.GetLocation(infoSpan)); } } } } // By definition, a tree is complete when all of its compiler diagnostics have been reported. // Since unused imports are the last thing we compute and report, a tree is complete when // the unused imports have been reported. if (EventQueue != null) { if (filterTree != null) { CompleteTree(filterTree); } else { foreach (var tree in SyntaxTrees) { CompleteTree(tree); } } } } internal void RecordImport(UsingDirectiveSyntax syntax) { RecordImportInternal(syntax); } internal void RecordImport(ExternAliasDirectiveSyntax syntax) { RecordImportInternal(syntax); } private void RecordImportInternal(CSharpSyntaxNode syntax) { LazyInitializer.EnsureInitialized(ref this.lazyImportInfos). Add(new ImportInfo(syntax.SyntaxTree, syntax.Kind(), syntax.Span)); } private struct ImportInfo : IEquatable { public readonly SyntaxTree Tree; public readonly SyntaxKind Kind; public readonly TextSpan Span; public ImportInfo(SyntaxTree tree, SyntaxKind kind, TextSpan span) { this.Tree = tree; this.Kind = kind; this.Span = span; } public override bool Equals(object obj) { return (obj is ImportInfo) && Equals((ImportInfo)obj); } public bool Equals(ImportInfo other) { return other.Kind == this.Kind && other.Tree == this.Tree && other.Span == this.Span; } public override int GetHashCode() { return Hash.Combine(Tree, Span.Start); } } #endregion #region Diagnostics internal override CommonMessageProvider MessageProvider { get { return CSharp.MessageProvider.Instance; } } /// /// The bag in which semantic analysis should deposit its diagnostics. /// internal DiagnosticBag SemanticDiagnostics { get { if (this.lazySemanticDiagnostics == null) { var diagnostics = new DiagnosticBag(); Interlocked.CompareExchange(ref this.lazySemanticDiagnostics, diagnostics, null); } return this.lazySemanticDiagnostics; } } private DiagnosticBag lazySemanticDiagnostics; /// /// A bag in which diagnostics that should be reported after code gen can be deposited. /// internal DiagnosticBag AdditionalCodegenWarnings { get { return this.additionalCodegenWarnings; } } private DiagnosticBag additionalCodegenWarnings = new DiagnosticBag(); internal DeclarationTable Declarations { get { return this.declarationTable; } } /// /// Gets the diagnostics produced during the parsing stage of a compilation. There are no diagnostics for declarations or accessor or /// method bodies, for example. /// public override ImmutableArray GetParseDiagnostics(CancellationToken cancellationToken = default(CancellationToken)) { return GetDiagnostics(CompilationStage.Parse, false, cancellationToken); } /// /// Gets the diagnostics produced during symbol declaration headers. There are no diagnostics for accessor or /// method bodies, for example. /// public override ImmutableArray GetDeclarationDiagnostics(CancellationToken cancellationToken = default(CancellationToken)) { return GetDiagnostics(CompilationStage.Declare, false, cancellationToken); } /// /// Gets the diagnostics produced during the analysis of method bodies and field initializers. /// public override ImmutableArray GetMethodBodyDiagnostics(CancellationToken cancellationToken = default(CancellationToken)) { return GetDiagnostics(CompilationStage.Compile, false, cancellationToken); } /// /// Gets the all the diagnostics for the compilation, including syntax, declaration, and binding. Does not /// include any diagnostics that might be produced during emit. /// public override ImmutableArray GetDiagnostics(CancellationToken cancellationToken = default(CancellationToken)) { return GetDiagnostics(DefaultDiagnosticsStage, true, cancellationToken); } internal ImmutableArray GetDiagnostics(CompilationStage stage, bool includeEarlierStages, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CSharp_Compilation_GetDiagnostics, message: this.AssemblyName, cancellationToken: cancellationToken)) { var builder = DiagnosticBag.GetInstance(); if (stage == CompilationStage.Parse || (stage > CompilationStage.Parse && includeEarlierStages)) { if (this.Options.ConcurrentBuild) { var parallelOptions = cancellationToken.CanBeCanceled ? new ParallelOptions() { CancellationToken = cancellationToken } : DefaultParallelOptions; Parallel.For(0, this.SyntaxTrees.Length, parallelOptions, UICultureUtilities.WithCurrentUICulture(i => builder.AddRange(this.SyntaxTrees[i].GetDiagnostics(cancellationToken)))); } else { foreach (var syntaxTree in this.SyntaxTrees) { cancellationToken.ThrowIfCancellationRequested(); builder.AddRange(syntaxTree.GetDiagnostics(cancellationToken)); } } } if (stage == CompilationStage.Declare || stage > CompilationStage.Declare && includeEarlierStages) { builder.AddRange(Options.Errors); cancellationToken.ThrowIfCancellationRequested(); // the set of diagnostics related to establishing references. builder.AddRange(GetBoundReferenceManager().Diagnostics); cancellationToken.ThrowIfCancellationRequested(); builder.AddRange(GetSourceDeclarationDiagnostics(cancellationToken: cancellationToken)); } cancellationToken.ThrowIfCancellationRequested(); if (stage == CompilationStage.Compile || stage > CompilationStage.Compile && includeEarlierStages) { var methodBodyDiagnostics = DiagnosticBag.GetInstance(); GetDiagnosticsForAllMethodBodies(methodBodyDiagnostics, cancellationToken); builder.AddRangeAndFree(methodBodyDiagnostics); } // Before returning diagnostics, we filter warnings // to honor the compiler options (e.g., /nowarn, /warnaserror and /warn) and the pragmas. var result = DiagnosticBag.GetInstance(); FilterAndAppendAndFreeDiagnostics(result, ref builder); return result.ToReadOnlyAndFree(); } } // Do the steps in compilation to get the method body diagnostics, but don't actually generate // IL or emit an assembly. private void GetDiagnosticsForAllMethodBodies(DiagnosticBag diagnostics, CancellationToken cancellationToken) { MethodCompiler.CompileMethodBodies( compilation: this, moduleBeingBuiltOpt: null, generateDebugInfo: false, hasDeclarationErrors: false, diagnostics: diagnostics, filterOpt: null, cancellationToken: cancellationToken); DocumentationCommentCompiler.WriteDocumentationCommentXml(this, null, null, diagnostics, cancellationToken); this.ReportUnusedImports(diagnostics, cancellationToken); } private static bool IsDefinedOrImplementedInSourceTree(Symbol symbol, SyntaxTree tree, TextSpan? span) { if (symbol.IsDefinedInSourceTree(tree, span)) { return true; } if (symbol.IsPartialDefinition()) { MethodSymbol implementationPart = ((MethodSymbol)symbol).PartialImplementationPart; if ((object)implementationPart != null) { return implementationPart.IsDefinedInSourceTree(tree, span); } } if (symbol.Kind == SymbolKind.Method && symbol.IsImplicitlyDeclared && ((MethodSymbol)symbol).MethodKind == MethodKind.Constructor) { // Include implicitly declared constructor if containing type is included return IsDefinedOrImplementedInSourceTree(symbol.ContainingType, tree, span); } return false; } private ImmutableArray GetDiagnosticsForMethodBodiesInTree(SyntaxTree tree, TextSpan? span, CancellationToken cancellationToken) { DiagnosticBag diagnostics = DiagnosticBag.GetInstance(); MethodCompiler.CompileMethodBodies( compilation: this, moduleBeingBuiltOpt: null, generateDebugInfo: false, hasDeclarationErrors: false, diagnostics: diagnostics, filterOpt: s => IsDefinedOrImplementedInSourceTree(s, tree, span), cancellationToken: cancellationToken); DocumentationCommentCompiler.WriteDocumentationCommentXml(this, null, null, diagnostics, cancellationToken, tree, span); // Report unused directives only if computing diagnostics for the entire tree. // Otherwise we cannot determine if a particular directive is used outside of the given sub-span within the tree. if (!span.HasValue || span.Value == tree.GetRoot(cancellationToken).FullSpan) { ReportUnusedImports(diagnostics, cancellationToken, tree); } return diagnostics.ToReadOnlyAndFree(); } /// /// Filter out warnings based on the compiler options (/nowarn, /warn and /warnaserror) and the pragma warning directives. /// 'incoming' is freed. /// /// True when there is no error or warning treated as an error. internal override bool FilterAndAppendAndFreeDiagnostics(DiagnosticBag accumulator, ref DiagnosticBag incoming) { bool result = FilterAndAppendDiagnostics(accumulator, incoming.AsEnumerableWithoutResolution()); incoming.Free(); incoming = null; return result; } internal override Diagnostic FilterDiagnostic(Diagnostic d) { return FilterDiagnostic(d, options); } private static Diagnostic FilterDiagnostic(Diagnostic d, CSharpCompilationOptions options) { return CSharpDiagnosticFilter.Filter(d, options.WarningLevel, options.GeneralDiagnosticOption, options.SpecificDiagnosticOptions); } /// /// Filter out warnings based on the compiler options (/nowarn, /warn and /warnaserror) and the pragma warning directives. /// /// True when there is no error. private bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable incoming) { bool hasError = false; foreach (Diagnostic d in incoming) { var filtered = FilterDiagnostic(d, this.options); if (filtered == null) { continue; } else if (filtered.Severity == DiagnosticSeverity.Error) { hasError = true; } accumulator.Add(filtered); } return !hasError; } private ImmutableArray GetSourceDeclarationDiagnostics(SyntaxTree syntaxTree = null, TextSpan? filterSpanWithinTree = null, Func, SyntaxTree, TextSpan?, IEnumerable> locationFilterOpt = null, CancellationToken cancellationToken = default(CancellationToken)) { // global imports diagnostics (specified via compilation options): GlobalImports.Complete(cancellationToken); SourceLocation location = null; if (syntaxTree != null) { var root = syntaxTree.GetRoot(cancellationToken); location = filterSpanWithinTree.HasValue ? new SourceLocation(syntaxTree, filterSpanWithinTree.Value) : new SourceLocation(root); } Assembly.ForceComplete(location, cancellationToken); var result = this.SemanticDiagnostics.AsEnumerable().Concat( ((SourceModuleSymbol)this.SourceModule).Diagnostics); if (locationFilterOpt != null) { Debug.Assert(syntaxTree != null); result = locationFilterOpt(result, syntaxTree, filterSpanWithinTree); } // NOTE: Concatenate the CLS diagnostics *after* filtering by tree/span, because they're already filtered. ImmutableArray clsDiagnostics = GetClsComplianceDiagnostics(syntaxTree, filterSpanWithinTree, cancellationToken); return result.AsImmutable().Concat(clsDiagnostics); } private ImmutableArray GetClsComplianceDiagnostics(SyntaxTree syntaxTree, TextSpan? filterSpanWithinTree, CancellationToken cancellationToken) { if (syntaxTree != null) { var builder = DiagnosticBag.GetInstance(); ClsComplianceChecker.CheckCompliance(this, builder, cancellationToken, syntaxTree, filterSpanWithinTree); return builder.ToReadOnlyAndFree(); } if (this.lazyClsComplianceDiagnostics.IsDefault) { var builder = DiagnosticBag.GetInstance(); ClsComplianceChecker.CheckCompliance(this, builder, cancellationToken); ImmutableInterlocked.InterlockedInitialize(ref this.lazyClsComplianceDiagnostics, builder.ToReadOnlyAndFree()); } Debug.Assert(!this.lazyClsComplianceDiagnostics.IsDefault); return this.lazyClsComplianceDiagnostics; } private static IEnumerable FilterDiagnosticsByLocation(IEnumerable diagnostics, SyntaxTree tree, TextSpan? filterSpanWithinTree) { foreach (var diagnostic in diagnostics) { if (diagnostic.ContainsLocation(tree, filterSpanWithinTree)) { yield return diagnostic; } } } internal ImmutableArray GetDiagnosticsForSyntaxTree( CompilationStage stage, SyntaxTree syntaxTree, TextSpan? filterSpanWithinTree, bool includeEarlierStages, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); var builder = DiagnosticBag.GetInstance(); if (stage == CompilationStage.Parse || (stage > CompilationStage.Parse && includeEarlierStages)) { var syntaxDiagnostics = syntaxTree.GetDiagnostics(); syntaxDiagnostics = FilterDiagnosticsByLocation(syntaxDiagnostics, syntaxTree, filterSpanWithinTree); builder.AddRange(syntaxDiagnostics); } cancellationToken.ThrowIfCancellationRequested(); if (stage == CompilationStage.Declare || (stage > CompilationStage.Declare && includeEarlierStages)) { var declarationDiagnostics = GetSourceDeclarationDiagnostics(syntaxTree, filterSpanWithinTree, FilterDiagnosticsByLocation, cancellationToken); Debug.Assert(declarationDiagnostics.All(d => d.ContainsLocation(syntaxTree, filterSpanWithinTree))); builder.AddRange(declarationDiagnostics); } cancellationToken.ThrowIfCancellationRequested(); if (stage == CompilationStage.Compile || (stage > CompilationStage.Compile && includeEarlierStages)) { //remove some errors that don't have locations in the tree, like "no suitable main method." //Members in trees other than the one being examined are not compiled. This includes field //initializers which can result in 'field is never initialized' warnings for fields in partial //types when the field is in a different source file than the one for which we're getting diagnostics. //For that reason the bag must be also filtered by tree. IEnumerable methodBodyDiagnostics = GetDiagnosticsForMethodBodiesInTree(syntaxTree, filterSpanWithinTree, cancellationToken); // TODO: Enable the below commented assert and remove the filtering code in the next line. // GetDiagnosticsForMethodBodiesInTree seems to be returning diagnostics with locations that don't satisfy the filter tree/span, this must be fixed. // Debug.Assert(methodBodyDiagnostics.All(d => DiagnosticContainsLocation(d, syntaxTree, filterSpanWithinTree))); methodBodyDiagnostics = FilterDiagnosticsByLocation(methodBodyDiagnostics, syntaxTree, filterSpanWithinTree); builder.AddRange(methodBodyDiagnostics); } // Before returning diagnostics, we filter warnings // to honor the compiler options (/nowarn, /warnaserror and /warn) and the pragmas. var result = DiagnosticBag.GetInstance(); FilterAndAppendAndFreeDiagnostics(result, ref builder); return result.ToReadOnlyAndFree(); } #endregion #region Resources protected override void AppendDefaultVersionResource(Stream resourceStream) { var sourceAssembly = SourceAssembly; string fileVersion = sourceAssembly.FileVersion ?? sourceAssembly.Identity.Version.ToString(); Win32ResourceConversions.AppendVersionToResourceStream(resourceStream, !this.Options.OutputKind.IsApplication(), fileVersion: fileVersion, originalFileName: this.SourceModule.Name, internalName: this.SourceModule.Name, productVersion: sourceAssembly.InformationalVersion ?? fileVersion, fileDescription: sourceAssembly.Title ?? " ", //alink would give this a blank if nothing was supplied. assemblyVersion: sourceAssembly.Identity.Version, legalCopyright: sourceAssembly.Copyright ?? " ", //alink would give this a blank if nothing was supplied. legalTrademarks: sourceAssembly.Trademark, productName: sourceAssembly.Product, comments: sourceAssembly.Description, companyName: sourceAssembly.Company); } #endregion #region Emit internal override bool IsDelaySign { get { return SourceAssembly.IsDelaySign; } } internal override StrongNameKeys StrongNameKeys { get { return SourceAssembly.StrongNameKeys; } } internal override FunctionId EmitFunctionId { get { return FunctionId.CSharp_Compilation_Emit; } } internal override CommonPEModuleBuilder CreateModuleBuilder( EmitOptions emitOptions, IEnumerable manifestResources, Func assemblySymbolMapper, CompilationTestData testData, DiagnosticBag diagnostics, CancellationToken cancellationToken) { return this.CreateModuleBuilder( emitOptions, manifestResources, assemblySymbolMapper, testData, diagnostics, ImmutableArray.Empty, cancellationToken); } internal CommonPEModuleBuilder CreateModuleBuilder( EmitOptions emitOptions, IEnumerable manifestResources, Func assemblySymbolMapper, CompilationTestData testData, DiagnosticBag diagnostics, ImmutableArray additionalTypes, CancellationToken cancellationToken) { // Do not waste a slot in the submission chain for submissions that contain no executable code // (they may only contain #r directives, usings, etc.) if (IsSubmission && !HasCodeToEmit()) { return null; } string runtimeMDVersion = GetRuntimeMetadataVersion(emitOptions, diagnostics); if (runtimeMDVersion == null) { return null; } var moduleProps = ConstructModuleSerializationProperties(emitOptions, runtimeMDVersion); if (manifestResources == null) { manifestResources = SpecializedCollections.EmptyEnumerable(); } PEModuleBuilder moduleBeingBuilt; if (this.options.OutputKind.IsNetModule()) { Debug.Assert(additionalTypes.IsEmpty); moduleBeingBuilt = new PENetModuleBuilder( (SourceModuleSymbol)SourceModule, emitOptions, moduleProps, manifestResources); } else { var kind = this.options.OutputKind.IsValid() ? this.options.OutputKind : OutputKind.DynamicallyLinkedLibrary; moduleBeingBuilt = new PEAssemblyBuilder( SourceAssembly, emitOptions, kind, moduleProps, manifestResources, assemblySymbolMapper, additionalTypes); } // testData is only passed when running tests. if (testData != null) { moduleBeingBuilt.SetMethodTestData(testData.Methods); testData.Module = moduleBeingBuilt; } return moduleBeingBuilt; } internal override bool CompileImpl( CommonPEModuleBuilder moduleBuilder, Stream win32Resources, Stream xmlDocStream, bool generateDebugInfo, DiagnosticBag diagnostics, Predicate filterOpt, CancellationToken cancellationToken) { // The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that the emitter // does not attempt to emit if there are declaration errors (but we do insert all errors from method body binding...) bool hasDeclarationErrors = !FilterAndAppendDiagnostics(diagnostics, GetDiagnostics(CompilationStage.Declare, true, cancellationToken)); // TODO (tomat): NoPIA: // EmbeddedSymbolManager.MarkAllDeferredSymbolsAsReferenced(this) var moduleBeingBuilt = (PEModuleBuilder)moduleBuilder; if (moduleBeingBuilt.EmitOptions.EmitMetadataOnly) { if (hasDeclarationErrors) { return false; } SynthesizedMetadataCompiler.ProcessSynthesizedMembers(this, moduleBeingBuilt, cancellationToken); } else { if (generateDebugInfo && moduleBeingBuilt != null) { if (!StartSourceChecksumCalculation(moduleBeingBuilt, diagnostics)) { return false; } } // Perform initial bind of method bodies in spite of earlier errors. This is the same // behavior as when calling GetDiagnostics() // Use a temporary bag so we don't have to refilter pre-existing diagnostics. DiagnosticBag methodBodyDiagnosticBag = DiagnosticBag.GetInstance(); MethodCompiler.CompileMethodBodies( this, moduleBeingBuilt, generateDebugInfo, hasDeclarationErrors, diagnostics: methodBodyDiagnosticBag, filterOpt: filterOpt, cancellationToken: cancellationToken); SetupWin32Resources(moduleBeingBuilt, win32Resources, methodBodyDiagnosticBag); ReportManifestResourceDuplicates( moduleBeingBuilt.ManifestResources, SourceAssembly.Modules.Skip(1).Select((m) => m.Name), //all modules except the first one AddedModulesResourceNames(methodBodyDiagnosticBag), methodBodyDiagnosticBag); bool hasMethodBodyErrorOrWarningAsError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag); if (hasDeclarationErrors || hasMethodBodyErrorOrWarningAsError) { return false; } } cancellationToken.ThrowIfCancellationRequested(); // Use a temporary bag so we don't have to refilter pre-existing diagnostics. DiagnosticBag xmlDiagnostics = DiagnosticBag.GetInstance(); string assemblyName = FileNameUtilities.ChangeExtension(moduleBeingBuilt.EmitOptions.OutputNameOverride, extension: null); DocumentationCommentCompiler.WriteDocumentationCommentXml(this, assemblyName, xmlDocStream, xmlDiagnostics, cancellationToken); if (!FilterAndAppendAndFreeDiagnostics(diagnostics, ref xmlDiagnostics)) { return false; } // Use a temporary bag so we don't have to refilter pre-existing diagnostics. DiagnosticBag importDiagnostics = DiagnosticBag.GetInstance(); this.ReportUnusedImports(importDiagnostics, cancellationToken); if (!FilterAndAppendAndFreeDiagnostics(diagnostics, ref importDiagnostics)) { Debug.Assert(false, "Should never produce an error"); return false; } return true; } // TODO: consider unifying with VB private bool StartSourceChecksumCalculation(PEModuleBuilder moduleBeingBuilt, DiagnosticBag diagnostics) { // Check that all syntax trees are debuggable: bool allTreesDebuggable = true; foreach (var tree in this.syntaxTrees) { if (!string.IsNullOrEmpty(tree.FilePath) && tree.GetText().Encoding == null) { diagnostics.Add(ErrorCode.ERR_EncodinglessSyntaxTree, tree.GetRoot().GetLocation()); allTreesDebuggable = false; } } if (!allTreesDebuggable) { return false; } // Add debug documents for all trees with distinct paths. foreach (var tree in this.syntaxTrees) { if (!string.IsNullOrEmpty(tree.FilePath)) { // compilation does not guarantee that all trees will have distinct paths. // Do not attempt adding a document for a particular path if we already added one. string normalizedPath = moduleBeingBuilt.NormalizeDebugDocumentPath(tree.FilePath, basePath: null); var existingDoc = moduleBeingBuilt.TryGetDebugDocumentForNormalizedPath(normalizedPath); if (existingDoc == null) { moduleBeingBuilt.AddDebugDocument(MakeDebugSourceDocumentForTree(normalizedPath, tree)); } } } // Add debug documents for all pragmas. // If there are clashes with already processed directives, report warnings. // If there are clashes with debug documents that came from actual trees, ignore the pragma. foreach (var tree in this.syntaxTrees) { AddDebugSourceDocumentsForChecksumDirectives(moduleBeingBuilt, tree, diagnostics); } return true; } private IEnumerable AddedModulesResourceNames(DiagnosticBag diagnostics) { ImmutableArray modules = SourceAssembly.Modules; for (int i = 1; i < modules.Length; i++) { var m = (Symbols.Metadata.PE.PEModuleSymbol)modules[i]; ImmutableArray resources; try { resources = m.Module.GetEmbeddedResourcesOrThrow(); } catch (BadImageFormatException) { diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, m), NoLocation.Singleton); continue; } foreach (var resource in resources) { yield return resource.Name; } } } internal override EmitDifferenceResult EmitDifference( EmitBaseline baseline, IEnumerable edits, Func isAddedSymbol, Stream metadataStream, Stream ilStream, Stream pdbStream, ICollection updatedMethods, CompilationTestData testData, CancellationToken cancellationToken) { return EmitHelpers.EmitDifference( this, baseline, edits, isAddedSymbol, metadataStream, ilStream, pdbStream, updatedMethods, testData, cancellationToken); } internal string GetRuntimeMetadataVersion(EmitOptions emitOptions, DiagnosticBag diagnostics) { string runtimeMDVersion = GetRuntimeMetadataVersion(emitOptions); if (runtimeMDVersion != null) { return runtimeMDVersion; } DiagnosticBag runtimeMDVersionDiagnostics = DiagnosticBag.GetInstance(); runtimeMDVersionDiagnostics.Add(ErrorCode.WRN_NoRuntimeMetadataVersion, NoLocation.Singleton); if (!FilterAndAppendAndFreeDiagnostics(diagnostics, ref runtimeMDVersionDiagnostics)) { return null; } return string.Empty; //prevent emitter from crashing. } private string GetRuntimeMetadataVersion(EmitOptions emitOptions) { var corAssembly = Assembly.CorLibrary as Symbols.Metadata.PE.PEAssemblySymbol; if ((object)corAssembly != null) { return corAssembly.Assembly.ManifestModule.MetadataVersion; } return emitOptions.RuntimeMetadataVersion; } private static void AddDebugSourceDocumentsForChecksumDirectives( PEModuleBuilder moduleBeingBuilt, SyntaxTree tree, DiagnosticBag diagnostics) { var checksumDirectives = tree.GetRoot().GetDirectives(d => d.Kind() == SyntaxKind.PragmaChecksumDirectiveTrivia && !d.ContainsDiagnostics); foreach (var directive in checksumDirectives) { var checksumDirective = (PragmaChecksumDirectiveTriviaSyntax)directive; var path = checksumDirective.File.ValueText; var checksumText = checksumDirective.Bytes.ValueText; var normalizedPath = moduleBeingBuilt.NormalizeDebugDocumentPath(path, basePath: tree.FilePath); var existingDoc = moduleBeingBuilt.TryGetDebugDocumentForNormalizedPath(normalizedPath); // duplicate checksum pragmas are valid as long as values match // if we have seen this document already, check for matching values. if (existingDoc != null) { // pragma matches a file path on an actual tree. // Dev12 compiler just ignores the pragma in this case which means that // checksum of the actual tree always wins and no warning is given. // We will continue doing the same. if (existingDoc.IsComputedChecksum) { continue; } var checksumAndAlgorithm = existingDoc.ChecksumAndAlgorithm; if (ChecksumMatches(checksumText, checksumAndAlgorithm.Item1)) { var guid = Guid.Parse(checksumDirective.Guid.ValueText); if (guid == checksumAndAlgorithm.Item2) { // all parts match, nothing to do continue; } } // did not match to an existing document // produce a warning and ignore the pragma diagnostics.Add(ErrorCode.WRN_ConflictingChecksum, new SourceLocation(checksumDirective), path); } else { var newDocument = new Cci.DebugSourceDocument( normalizedPath, Cci.DebugSourceDocument.CorSymLanguageTypeCSharp, MakeChecksumBytes(checksumDirective.Bytes.ValueText), Guid.Parse(checksumDirective.Guid.ValueText)); moduleBeingBuilt.AddDebugDocument(newDocument); } } } private static bool ChecksumMatches(string bytesText, ImmutableArray bytes) { if (bytesText.Length != bytes.Length * 2) { return false; } for (int i = 0, len = bytesText.Length / 2; i < len; i++) { // 1A in text becomes 0x1A var b = SyntaxFacts.HexValue(bytesText[i * 2]) * 16 + SyntaxFacts.HexValue(bytesText[i * 2 + 1]); if (b != bytes[i]) { return false; } } return true; } private static ImmutableArray MakeChecksumBytes(string bytesText) { int length = bytesText.Length / 2; var builder = ArrayBuilder.GetInstance(length); for (int i = 0; i < length; i++) { // 1A in text becomes 0x1A var b = SyntaxFacts.HexValue(bytesText[i * 2]) * 16 + SyntaxFacts.HexValue(bytesText[i * 2 + 1]); builder.Add((byte)b); } return builder.ToImmutableAndFree(); } private static Cci.DebugSourceDocument MakeDebugSourceDocumentForTree(string normalizedPath, SyntaxTree tree) { return new Cci.DebugSourceDocument(normalizedPath, Cci.DebugSourceDocument.CorSymLanguageTypeCSharp, () => tree.GetChecksumAndAlgorithm()); } private void SetupWin32Resources(PEModuleBuilder moduleBeingBuilt, Stream win32Resources, DiagnosticBag diagnostics) { if (win32Resources == null) return; switch (DetectWin32ResourceForm(win32Resources)) { case Win32ResourceForm.COFF: moduleBeingBuilt.Win32ResourceSection = MakeWin32ResourcesFromCOFF(win32Resources, diagnostics); break; case Win32ResourceForm.RES: moduleBeingBuilt.Win32Resources = MakeWin32ResourceList(win32Resources, diagnostics); break; default: diagnostics.Add(ErrorCode.ERR_BadWin32Res, NoLocation.Singleton, "Unrecognized file format."); break; } } protected override bool HasCodeToEmit() { foreach (var syntaxTree in SyntaxTrees) { var unit = syntaxTree.GetCompilationUnitRoot(); if (unit.Members.Count > 0) { return true; } } return false; } #endregion #region Common Members protected override Compilation CommonWithReferences(IEnumerable newReferences) { return WithReferences(newReferences); } protected override Compilation CommonWithAssemblyName(string assemblyName) { return WithAssemblyName(assemblyName); } protected override ITypeSymbol CommonGetSubmissionResultType(out bool hasValue) { return GetSubmissionResultType(out hasValue); } protected override IAssemblySymbol CommonAssembly { get { return this.Assembly; } } protected override INamespaceSymbol CommonGlobalNamespace { get { return this.GlobalNamespace; } } protected override CompilationOptions CommonOptions { get { return options; } } protected override Compilation CommonPreviousSubmission { get { return previousSubmission; } } protected override SemanticModel CommonGetSemanticModel(SyntaxTree syntaxTree) { return this.GetSemanticModel((SyntaxTree)syntaxTree); } protected override IEnumerable CommonSyntaxTrees { get { return this.SyntaxTrees; } } protected override Compilation CommonAddSyntaxTrees(IEnumerable trees) { var array = trees as SyntaxTree[]; if (array != null) { return this.AddSyntaxTrees(array); } if (trees == null) { throw new ArgumentNullException("trees"); } return this.AddSyntaxTrees(trees.Cast()); } protected override Compilation CommonRemoveSyntaxTrees(IEnumerable trees) { var array = trees as SyntaxTree[]; if (array != null) { return this.RemoveSyntaxTrees(array); } if (trees == null) { throw new ArgumentNullException("trees"); } return this.RemoveSyntaxTrees(trees.Cast()); } protected override Compilation CommonRemoveAllSyntaxTrees() { return this.RemoveAllSyntaxTrees(); } protected override Compilation CommonReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree) { return this.ReplaceSyntaxTree((SyntaxTree)oldTree, (SyntaxTree)newTree); } protected override Compilation CommonWithOptions(CompilationOptions options) { return this.WithOptions((CSharpCompilationOptions)options); } protected override Compilation CommonWithPreviousSubmission(Compilation newPreviousSubmission) { return this.WithPreviousSubmission((CSharpCompilation)newPreviousSubmission); } protected override bool CommonContainsSyntaxTree(SyntaxTree syntaxTree) { return this.ContainsSyntaxTree((SyntaxTree)syntaxTree); } protected override ISymbol CommonGetAssemblyOrModuleSymbol(MetadataReference reference) { return this.GetAssemblyOrModuleSymbol(reference); } protected override Compilation CommonClone() { return this.Clone(); } protected override IModuleSymbol CommonSourceModule { get { return this.SourceModule; } } protected override INamedTypeSymbol CommonGetSpecialType(SpecialType specialType) { return this.GetSpecialType(specialType); } protected override INamespaceSymbol CommonGetCompilationNamespace(INamespaceSymbol namespaceSymbol) { return this.GetCompilationNamespace(namespaceSymbol); } protected override INamedTypeSymbol CommonGetTypeByMetadataName(string metadataName) { return this.GetTypeByMetadataName(metadataName); } protected override INamedTypeSymbol CommonScriptClass { get { return this.ScriptClass; } } protected override IArrayTypeSymbol CommonCreateArrayTypeSymbol(ITypeSymbol elementType, int rank) { return CreateArrayTypeSymbol(elementType.EnsureCSharpSymbolOrNull("elementType"), rank); } protected override IPointerTypeSymbol CommonCreatePointerTypeSymbol(ITypeSymbol elementType) { return CreatePointerTypeSymbol(elementType.EnsureCSharpSymbolOrNull("elementType")); } protected override ITypeSymbol CommonDynamicType { get { return DynamicType; } } protected override INamedTypeSymbol CommonObjectType { get { return this.ObjectType; } } protected override MetadataReference CommonGetMetadataReference(IAssemblySymbol assemblySymbol) { var symbol = assemblySymbol as AssemblySymbol; if ((object)symbol != null) { return this.GetMetadataReference(symbol); } else { return null; } } protected override IMethodSymbol CommonGetEntryPoint(CancellationToken cancellationToken) { return this.GetEntryPoint(cancellationToken); } internal override int CompareSourceLocations(Location loc1, Location loc2) { Debug.Assert(loc1.IsInSource); Debug.Assert(loc2.IsInSource); var comparison = CompareSyntaxTreeOrdering(loc1.SourceTree, loc2.SourceTree); if (comparison != 0) { return comparison; } return loc1.SourceSpan.Start - loc2.SourceSpan.Start; } /// /// Return true if there is a source declaration symbol name that meets given predicate. /// public override bool ContainsSymbolsWithName(Func predicate, SymbolFilter filter = SymbolFilter.TypeAndMember, CancellationToken cancellationToken = default(CancellationToken)) { if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } if (filter == SymbolFilter.None) { throw new ArgumentException(CSharpResources.NoNoneSearchCriteria, nameof(filter)); } return this.declarationTable.ContainsName(predicate, filter, cancellationToken); } /// /// Return source declaration symbols whose name meets given predicate. /// public override IEnumerable GetSymbolsWithName(Func predicate, SymbolFilter filter = SymbolFilter.TypeAndMember, CancellationToken cancellationToken = default(CancellationToken)) { if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } if (filter == SymbolFilter.None) { throw new ArgumentException(CSharpResources.NoNoneSearchCriteria, nameof(filter)); } return new SymbolSearcher(this).GetSymbolsWithName(predicate, filter, cancellationToken); } #endregion internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray analyzers, AnalyzerOptions options, Func continueOnAnalyzerException, CancellationToken cancellationToken) { return new AnalyzerDriver(analyzers, n => n.Kind(), options, continueOnAnalyzerException, cancellationToken); } internal void SymbolDeclaredEvent(Symbol symbol) { if (EventQueue != null) EventQueue.Enqueue(new SymbolDeclaredCompilationEvent(this, symbol)); } /// /// Determine if enum arrays can be initialized using block initialization. /// /// True if it's safe to use block initialization for enum arrays. /// /// In NetFx 4.0, block array initializers do not work on all combinations of {32/64 X Debug/Retail} when array elements are enums. /// This is fixed in 4.5 thus enabling block array initialization for a very common case. /// We look for the presence of which was introduced in .Net 4.5 /// internal bool EnableEnumArrayBlockInitialization { get { var sustainedLowLatency = GetWellKnownTypeMember(WellKnownMember.System_Runtime_GCLatencyMode__SustainedLowLatency); return sustainedLowLatency != null && sustainedLowLatency.ContainingAssembly == Assembly.CorLibrary; } } private class SymbolSearcher { private readonly Dictionary cache; private readonly CSharpCompilation compilation; public SymbolSearcher(CSharpCompilation compilation) { this.cache = new Dictionary(); this.compilation = compilation; } public IEnumerable GetSymbolsWithName(Func predicate, SymbolFilter filter, CancellationToken cancellationToken) { var result = new HashSet(); var spine = new List(); AppendSymbolsWithName(spine, this.compilation.declarationTable.MergedRoot, predicate, filter, result, cancellationToken); return result; } private void AppendSymbolsWithName( List spine, MergedNamespaceOrTypeDeclaration current, Func predicate, SymbolFilter filter, HashSet set, CancellationToken cancellationToken) { var includeNamespace = (filter & SymbolFilter.Namespace) == SymbolFilter.Namespace; var includeType = (filter & SymbolFilter.Type) == SymbolFilter.Type; var includeMember = (filter & SymbolFilter.Member) == SymbolFilter.Member; if (current.Kind == DeclarationKind.Namespace) { if (includeNamespace && predicate(current.Name)) { var container = GetSpineSymbol(spine); set.Add(GetSymbol(container, current)); } } else { if (includeType && predicate(current.Name)) { var container = GetSpineSymbol(spine); set.Add(GetSymbol(container, current)); } if (includeMember) { AppendMemberSymbolsWithName(spine, current, predicate, set, cancellationToken); } } spine.Add(current); foreach (var child in current.Children.OfType()) { if (includeMember || includeType) { AppendSymbolsWithName(spine, child, predicate, filter, set, cancellationToken); continue; } if (child.Kind == DeclarationKind.Namespace) { AppendSymbolsWithName(spine, child, predicate, filter, set, cancellationToken); } } // pop last one spine.RemoveAt(spine.Count - 1); } private void AppendMemberSymbolsWithName( List spine, MergedNamespaceOrTypeDeclaration current, Func predicate, HashSet set, CancellationToken cancellationToken) { spine.Add(current); var container = GetSpineSymbol(spine); foreach (var member in container.GetMembers()) { if (!member.IsTypeOrTypeAlias() && (member.CanBeReferencedByName || member.IsExplicitInterfaceImplementation() || member.IsIndexer()) && predicate(member.Name)) { set.Add(member); } } spine.RemoveAt(spine.Count - 1); } private NamespaceOrTypeSymbol GetSpineSymbol(List spine) { if (spine.Count == 0) { return null; } var symbol = GetCachedSymbol(spine[spine.Count - 1]); if (symbol != null) { return symbol; } var current = this.compilation.GlobalNamespace as NamespaceOrTypeSymbol; for (var i = 1; i < spine.Count; i++) { current = GetSymbol(current, spine[i]); } return current; } private NamespaceOrTypeSymbol GetCachedSymbol(MergedNamespaceOrTypeDeclaration declaration) { NamespaceOrTypeSymbol symbol; if (this.cache.TryGetValue(declaration, out symbol)) { return symbol; } return null; } private NamespaceOrTypeSymbol GetSymbol(NamespaceOrTypeSymbol container, MergedNamespaceOrTypeDeclaration declaration) { if (container == null) { return this.compilation.GlobalNamespace; } if (declaration.Kind == DeclarationKind.Namespace) { AddCache(container.GetMembers(declaration.Name).OfType()); } else { AddCache(container.GetTypeMembers(declaration.Name)); } return GetCachedSymbol(declaration); } private void AddCache(IEnumerable symbols) { foreach (var symbol in symbols) { var mergedNamespace = symbol as MergedNamespaceSymbol; if (mergedNamespace != null) { this.cache[mergedNamespace.ConstituentNamespaces.OfType().First().MergedDeclaration] = symbol; continue; } var sourceNamespace = symbol as SourceNamespaceSymbol; if (sourceNamespace != null) { this.cache[sourceNamespace.MergedDeclaration] = sourceNamespace; continue; } var sourceType = symbol as SourceMemberContainerTypeSymbol; if (sourceType != null) { this.cache[sourceType.MergedDeclaration] = sourceType; } } } } } }