From 3f1a7557498156bce83bfc9154ae6c75f97eee37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Wed, 10 Aug 2016 12:58:56 -0700 Subject: [PATCH] Unify code creating debug source documents across C# and VB (#12968) --- .../Portable/Compilation/CSharpCompilation.cs | 65 ++--------- .../Portable/Compiler/MethodCompiler.cs | 2 +- .../Portable/Emitter/Model/PEModuleBuilder.cs | 5 +- .../CSharp/Portable/Errors/MessageProvider.cs | 107 +++++++++--------- .../Core/Portable/CodeAnalysis.csproj | 1 + .../Core/Portable/Compilation/Compilation.cs | 52 +++++++++ .../Diagnostic/CommonMessageProvider.cs | 1 + .../Portable/Emit/CommonPEModuleBuilder.cs | 65 +---------- .../Portable/Emit/DebugDocumentsBuilder.cs | 75 ++++++++++++ .../Portable/Compilation/MethodCompiler.vb | 2 +- .../Compilation/VisualBasicCompilation.vb | 60 ++-------- .../Portable/Emit/PEModuleBuilder.vb | 2 +- .../Portable/Errors/MessageProvider.vb | 6 + .../Shared/Mocks/TestMessageProvider.cs | 5 + 14 files changed, 223 insertions(+), 225 deletions(-) create mode 100644 src/Compilers/Core/Portable/Emit/DebugDocumentsBuilder.cs diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 8dd6b319b49..d5a7b769305 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2314,7 +2314,7 @@ internal override StrongNameKeys StrongNameKeys } else { - if ((emittingPdb || emitOptions.EmitDynamicAnalysisData) && !StartSourceChecksumCalculation(moduleBeingBuilt, diagnostics)) + if ((emittingPdb || emitOptions.EmitDynamicAnalysisData) && !StartSourceChecksumCalculation(moduleBeingBuilt.DebugDocumentsBuilder, diagnostics)) { return false; } @@ -2381,54 +2381,6 @@ internal override StrongNameKeys StrongNameKeys return FilterAndAppendAndFreeDiagnostics(diagnostics, ref xmlDiagnostics); } - // TODO: consider unifying with VB - private bool StartSourceChecksumCalculation(PEModuleBuilder moduleBeingBuilt, DiagnosticBag diagnostics) - { - var syntaxTrees = this.SyntaxTrees; - - // Check that all syntax trees are debuggable: - bool allTreesDebuggable = true; - foreach (var tree in 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 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 syntaxTrees) - { - AddDebugSourceDocumentsForChecksumDirectives(moduleBeingBuilt, tree, diagnostics); - } - - return true; - } - private IEnumerable AddedModulesResourceNames(DiagnosticBag diagnostics) { ImmutableArray modules = SourceAssembly.Modules; @@ -2509,8 +2461,8 @@ private string GetRuntimeMetadataVersion(EmitOptions emitOptions) return emitOptions.RuntimeMetadataVersion; } - private static void AddDebugSourceDocumentsForChecksumDirectives( - PEModuleBuilder moduleBeingBuilt, + internal override void AddDebugSourceDocumentsForChecksumDirectives( + DebugDocumentsBuilder documentsBuilder, SyntaxTree tree, DiagnosticBag diagnostics) { @@ -2523,8 +2475,8 @@ private string GetRuntimeMetadataVersion(EmitOptions emitOptions) var path = checksumDirective.File.ValueText; var checksumText = checksumDirective.Bytes.ValueText; - var normalizedPath = moduleBeingBuilt.NormalizeDebugDocumentPath(path, basePath: tree.FilePath); - var existingDoc = moduleBeingBuilt.TryGetDebugDocumentForNormalizedPath(normalizedPath); + var normalizedPath = documentsBuilder.NormalizeDebugDocumentPath(path, basePath: tree.FilePath); + var existingDoc = documentsBuilder.TryGetDebugDocumentForNormalizedPath(normalizedPath); // duplicate checksum pragmas are valid as long as values match // if we have seen this document already, check for matching values. @@ -2562,7 +2514,7 @@ private string GetRuntimeMetadataVersion(EmitOptions emitOptions) MakeChecksumBytes(checksumDirective.Bytes.ValueText), Guid.Parse(checksumDirective.Guid.ValueText)); - moduleBeingBuilt.AddDebugDocument(newDocument); + documentsBuilder.AddDebugDocument(newDocument); } } } @@ -2606,10 +2558,7 @@ private static ImmutableArray MakeChecksumBytes(string bytesText) return builder.ToImmutableAndFree(); } - private static Cci.DebugSourceDocument MakeDebugSourceDocumentForTree(string normalizedPath, SyntaxTree tree) - { - return new Cci.DebugSourceDocument(normalizedPath, Cci.DebugSourceDocument.CorSymLanguageTypeCSharp, () => tree.GetChecksumAndAlgorithm()); - } + internal override Guid DebugSourceDocumentLanguageId => Cci.DebugSourceDocument.CorSymLanguageTypeCSharp; internal override bool HasCodeToEmit() { diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 7f2d5086213..67ba7f3fb2b 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -91,7 +91,7 @@ private void SetGlobalErrorIfTrue(bool arg) if (emittingPdb || moduleBeingBuiltOpt?.EmitOptions.EmitDynamicAnalysisData == true) { - _debugDocumentProvider = (path, basePath) => moduleBeingBuiltOpt.GetOrAddDebugDocument(path, basePath, CreateDebugDocumentForFile); + _debugDocumentProvider = (path, basePath) => moduleBeingBuiltOpt.DebugDocumentsBuilder.GetOrAddDebugDocument(path, basePath, CreateDebugDocumentForFile); } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 7d066e113ec..c78209d7bdb 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -9,7 +9,6 @@ using System.Reflection; using System.Reflection.PortableExecutable; using System.Threading; -using Microsoft.Cci; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Emit; @@ -289,7 +288,7 @@ private void AddSymbolLocation(MultiDictionary (int)ErrorCode.ERR_CantMakeTempFile; // command line: - public override int ERR_ExpectedSingleScript { get { return (int)ErrorCode.ERR_ExpectedSingleScript; } } - public override int ERR_OpenResponseFile { get { return (int)ErrorCode.ERR_OpenResponseFile; } } - public override int ERR_InvalidPathMap { get { return (int)ErrorCode.ERR_InvalidPathMap; } } - public override int FTL_InputFileNameTooLong { get { return (int)ErrorCode.FTL_InputFileNameTooLong; } } - public override int ERR_FileNotFound { get { return (int)ErrorCode.ERR_FileNotFound; } } - public override int ERR_NoSourceFile { get { return (int)ErrorCode.ERR_NoSourceFile; } } - public override int ERR_CantOpenFileWrite { get { return (int)ErrorCode.ERR_CantOpenFileWrite; } } - public override int ERR_OutputWriteFailed { get { return (int)ErrorCode.ERR_OutputWriteFailed; } } - public override int WRN_NoConfigNotOnCommandLine { get { return (int)ErrorCode.WRN_NoConfigNotOnCommandLine; } } - public override int ERR_BinaryFile { get { return (int)ErrorCode.ERR_BinaryFile; } } - public override int WRN_AnalyzerCannotBeCreated { get { return (int)ErrorCode.WRN_AnalyzerCannotBeCreated; } } - public override int WRN_NoAnalyzerInAssembly { get { return (int)ErrorCode.WRN_NoAnalyzerInAssembly; } } - public override int WRN_UnableToLoadAnalyzer { get { return (int)ErrorCode.WRN_UnableToLoadAnalyzer; } } - public override int INF_UnableToLoadSomeTypesInAnalyzer { get { return (int)ErrorCode.INF_UnableToLoadSomeTypesInAnalyzer; } } - public override int ERR_CantReadRulesetFile { get { return (int)ErrorCode.ERR_CantReadRulesetFile; } } - public override int ERR_CompileCancelled { get { return (int)ErrorCode.ERR_CompileCancelled; } } + public override int ERR_ExpectedSingleScript => (int)ErrorCode.ERR_ExpectedSingleScript; + public override int ERR_OpenResponseFile => (int)ErrorCode.ERR_OpenResponseFile; + public override int ERR_InvalidPathMap => (int)ErrorCode.ERR_InvalidPathMap; + public override int FTL_InputFileNameTooLong => (int)ErrorCode.FTL_InputFileNameTooLong; + public override int ERR_FileNotFound => (int)ErrorCode.ERR_FileNotFound; + public override int ERR_NoSourceFile => (int)ErrorCode.ERR_NoSourceFile; + public override int ERR_CantOpenFileWrite => (int)ErrorCode.ERR_CantOpenFileWrite; + public override int ERR_OutputWriteFailed => (int)ErrorCode.ERR_OutputWriteFailed; + public override int WRN_NoConfigNotOnCommandLine => (int)ErrorCode.WRN_NoConfigNotOnCommandLine; + public override int ERR_BinaryFile => (int)ErrorCode.ERR_BinaryFile; + public override int WRN_AnalyzerCannotBeCreated => (int)ErrorCode.WRN_AnalyzerCannotBeCreated; + public override int WRN_NoAnalyzerInAssembly => (int)ErrorCode.WRN_NoAnalyzerInAssembly; + public override int WRN_UnableToLoadAnalyzer => (int)ErrorCode.WRN_UnableToLoadAnalyzer; + public override int INF_UnableToLoadSomeTypesInAnalyzer => (int)ErrorCode.INF_UnableToLoadSomeTypesInAnalyzer; + public override int ERR_CantReadRulesetFile => (int)ErrorCode.ERR_CantReadRulesetFile; + public override int ERR_CompileCancelled => (int)ErrorCode.ERR_CompileCancelled; // compilation options: - public override int ERR_BadCompilationOptionValue { get { return (int)ErrorCode.ERR_BadCompilationOptionValue; } } + public override int ERR_BadCompilationOptionValue => (int)ErrorCode.ERR_BadCompilationOptionValue; public override int ERR_MutuallyExclusiveOptions => (int)ErrorCode.ERR_MutuallyExclusiveOptions; // emit options: - public override int ERR_InvalidDebugInformationFormat { get { return (int)ErrorCode.ERR_InvalidDebugInformationFormat; } } - public override int ERR_InvalidOutputName { get { return (int)ErrorCode.ERR_InvalidOutputName; } } - public override int ERR_InvalidFileAlignment { get { return (int)ErrorCode.ERR_InvalidFileAlignment; } } - public override int ERR_InvalidSubsystemVersion { get { return (int)ErrorCode.ERR_InvalidSubsystemVersion; } } + public override int ERR_InvalidDebugInformationFormat => (int)ErrorCode.ERR_InvalidDebugInformationFormat; + public override int ERR_InvalidOutputName => (int)ErrorCode.ERR_InvalidOutputName; + public override int ERR_InvalidFileAlignment => (int)ErrorCode.ERR_InvalidFileAlignment; + public override int ERR_InvalidSubsystemVersion => (int)ErrorCode.ERR_InvalidSubsystemVersion; // reference manager: - public override int ERR_MetadataFileNotAssembly { get { return (int)ErrorCode.ERR_ImportNonAssembly; } } - public override int ERR_MetadataFileNotModule { get { return (int)ErrorCode.ERR_AddModuleAssembly; } } - public override int ERR_InvalidAssemblyMetadata { get { return (int)ErrorCode.FTL_MetadataCantOpenFile; } } - public override int ERR_InvalidModuleMetadata { get { return (int)ErrorCode.FTL_MetadataCantOpenFile; } } - public override int ERR_ErrorOpeningAssemblyFile { get { return (int)ErrorCode.FTL_MetadataCantOpenFile; } } - public override int ERR_ErrorOpeningModuleFile { get { return (int)ErrorCode.FTL_MetadataCantOpenFile; } } - public override int ERR_MetadataFileNotFound { get { return (int)ErrorCode.ERR_NoMetadataFile; } } - public override int ERR_MetadataReferencesNotSupported { get { return (int)ErrorCode.ERR_MetadataReferencesNotSupported; } } - public override int ERR_LinkedNetmoduleMetadataMustProvideFullPEImage { get { return (int)ErrorCode.ERR_LinkedNetmoduleMetadataMustProvideFullPEImage; } } + public override int ERR_MetadataFileNotAssembly => (int)ErrorCode.ERR_ImportNonAssembly; + public override int ERR_MetadataFileNotModule => (int)ErrorCode.ERR_AddModuleAssembly; + public override int ERR_InvalidAssemblyMetadata => (int)ErrorCode.FTL_MetadataCantOpenFile; + public override int ERR_InvalidModuleMetadata => (int)ErrorCode.FTL_MetadataCantOpenFile; + public override int ERR_ErrorOpeningAssemblyFile => (int)ErrorCode.FTL_MetadataCantOpenFile; + public override int ERR_ErrorOpeningModuleFile => (int)ErrorCode.FTL_MetadataCantOpenFile; + public override int ERR_MetadataFileNotFound => (int)ErrorCode.ERR_NoMetadataFile; + public override int ERR_MetadataReferencesNotSupported => (int)ErrorCode.ERR_MetadataReferencesNotSupported; + public override int ERR_LinkedNetmoduleMetadataMustProvideFullPEImage => (int)ErrorCode.ERR_LinkedNetmoduleMetadataMustProvideFullPEImage; public override void ReportDuplicateMetadataReferenceStrong(DiagnosticBag diagnostics, Location location, MetadataReference reference, AssemblyIdentity identity, MetadataReference equivalentReference, AssemblyIdentity equivalentIdentity) { @@ -177,36 +177,37 @@ public override void ReportDuplicateMetadataReferenceWeak(DiagnosticBag diagnost } // signing: - public override int ERR_PublicKeyFileFailure { get { return (int)ErrorCode.ERR_PublicKeyFileFailure; } } - public override int ERR_PublicKeyContainerFailure { get { return (int)ErrorCode.ERR_PublicKeyContainerFailure; } } - public override int ERR_OptionMustBeAbsolutePath { get { return (int)ErrorCode.ERR_OptionMustBeAbsolutePath; } } + public override int ERR_PublicKeyFileFailure => (int)ErrorCode.ERR_PublicKeyFileFailure; + public override int ERR_PublicKeyContainerFailure => (int)ErrorCode.ERR_PublicKeyContainerFailure; + public override int ERR_OptionMustBeAbsolutePath => (int)ErrorCode.ERR_OptionMustBeAbsolutePath; // resources: - public override int ERR_CantReadResource { get { return (int)ErrorCode.ERR_CantReadResource; } } - public override int ERR_CantOpenWin32Resource { get { return (int)ErrorCode.ERR_CantOpenWin32Res; } } - public override int ERR_CantOpenWin32Manifest { get { return (int)ErrorCode.ERR_CantOpenWin32Manifest; } } - public override int ERR_CantOpenWin32Icon { get { return (int)ErrorCode.ERR_CantOpenIcon; } } - public override int ERR_ErrorBuildingWin32Resource { get { return (int)ErrorCode.ERR_ErrorBuildingWin32Resources; } } - public override int ERR_BadWin32Resource { get { return (int)ErrorCode.ERR_BadWin32Res; } } - public override int ERR_ResourceFileNameNotUnique { get { return (int)ErrorCode.ERR_ResourceFileNameNotUnique; } } - public override int ERR_ResourceNotUnique { get { return (int)ErrorCode.ERR_ResourceNotUnique; } } - public override int ERR_ResourceInModule { get { return (int)ErrorCode.ERR_CantRefResource; } } + public override int ERR_CantReadResource => (int)ErrorCode.ERR_CantReadResource; + public override int ERR_CantOpenWin32Resource => (int)ErrorCode.ERR_CantOpenWin32Res; + public override int ERR_CantOpenWin32Manifest => (int)ErrorCode.ERR_CantOpenWin32Manifest; + public override int ERR_CantOpenWin32Icon => (int)ErrorCode.ERR_CantOpenIcon; + public override int ERR_ErrorBuildingWin32Resource => (int)ErrorCode.ERR_ErrorBuildingWin32Resources; + public override int ERR_BadWin32Resource => (int)ErrorCode.ERR_BadWin32Res; + public override int ERR_ResourceFileNameNotUnique => (int)ErrorCode.ERR_ResourceFileNameNotUnique; + public override int ERR_ResourceNotUnique => (int)ErrorCode.ERR_ResourceNotUnique; + public override int ERR_ResourceInModule => (int)ErrorCode.ERR_CantRefResource; // pseudo-custom attributes: - public override int ERR_PermissionSetAttributeFileReadError { get { return (int)ErrorCode.ERR_PermissionSetAttributeFileReadError; } } + public override int ERR_PermissionSetAttributeFileReadError => (int)ErrorCode.ERR_PermissionSetAttributeFileReadError; // PDB Writer: - public override int WRN_PdbUsingNameTooLong { get { return (int)ErrorCode.WRN_DebugFullNameTooLong; } } - public override int WRN_PdbLocalNameTooLong { get { return (int)ErrorCode.WRN_PdbLocalNameTooLong; } } - public override int ERR_PdbWritingFailed { get { return (int)ErrorCode.FTL_DebugEmitFailure; } } + public override int ERR_EncodinglessSyntaxTree => (int)ErrorCode.ERR_EncodinglessSyntaxTree; + public override int WRN_PdbUsingNameTooLong => (int)ErrorCode.WRN_DebugFullNameTooLong; + public override int WRN_PdbLocalNameTooLong => (int)ErrorCode.WRN_PdbLocalNameTooLong; + public override int ERR_PdbWritingFailed => (int)ErrorCode.FTL_DebugEmitFailure; // PE Writer: - public override int ERR_MetadataNameTooLong { get { return (int)ErrorCode.ERR_MetadataNameTooLong; } } - public override int ERR_EncReferenceToAddedMember { get { return (int)ErrorCode.ERR_EncReferenceToAddedMember; } } - public override int ERR_TooManyUserStrings { get { return (int)ErrorCode.ERR_TooManyUserStrings; } } - public override int ERR_PeWritingFailure { get { return (int)ErrorCode.ERR_PeWritingFailure; } } - public override int ERR_ModuleEmitFailure { get { return (int)ErrorCode.ERR_ModuleEmitFailure; } } - public override int ERR_EncUpdateFailedMissingAttribute { get { return (int)ErrorCode.ERR_EncUpdateFailedMissingAttribute; } } + public override int ERR_MetadataNameTooLong => (int)ErrorCode.ERR_MetadataNameTooLong; + public override int ERR_EncReferenceToAddedMember => (int)ErrorCode.ERR_EncReferenceToAddedMember; + public override int ERR_TooManyUserStrings => (int)ErrorCode.ERR_TooManyUserStrings; + public override int ERR_PeWritingFailure => (int)ErrorCode.ERR_PeWritingFailure; + public override int ERR_ModuleEmitFailure => (int)ErrorCode.ERR_ModuleEmitFailure; + public override int ERR_EncUpdateFailedMissingAttribute => (int)ErrorCode.ERR_EncUpdateFailedMissingAttribute; public override void ReportInvalidAttributeArgument(DiagnosticBag diagnostics, SyntaxNode attributeSyntax, int parameterIndex, AttributeData attribute) { diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index 7282363d22b..60517675029 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -51,6 +51,7 @@ + diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index ca2eb71e95c..4e6a18b2388 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1527,6 +1527,58 @@ internal bool IsRealSigned Predicate filterOpt, CancellationToken cancellationToken); + internal bool StartSourceChecksumCalculation(DebugDocumentsBuilder documentsBuilder, DiagnosticBag diagnostics) + { + // Check that all syntax trees are debuggable: + bool allTreesDebuggable = true; + foreach (var tree in SyntaxTrees) + { + if (!string.IsNullOrEmpty(tree.FilePath) && tree.GetText().Encoding == null) + { + diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_EncodinglessSyntaxTree, tree.GetRoot().GetLocation())); + allTreesDebuggable = false; + } + } + + if (!allTreesDebuggable) + { + return false; + } + + // Add debug documents for all trees with distinct paths. + foreach (var tree in 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 = documentsBuilder.NormalizeDebugDocumentPath(tree.FilePath, basePath: null); + var existingDoc = documentsBuilder.TryGetDebugDocumentForNormalizedPath(normalizedPath); + if (existingDoc == null) + { + documentsBuilder.AddDebugDocument(new Cci.DebugSourceDocument( + normalizedPath, + DebugSourceDocumentLanguageId, + () => tree.GetChecksumAndAlgorithm())); + } + } + } + + // 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 SyntaxTrees) + { + AddDebugSourceDocumentsForChecksumDirectives(documentsBuilder, tree, diagnostics); + } + + return true; + } + + internal abstract Guid DebugSourceDocumentLanguageId { get; } + + internal abstract void AddDebugSourceDocumentsForChecksumDirectives(DebugDocumentsBuilder documentsBuilder, SyntaxTree tree, DiagnosticBag diagnostics); + /// /// Update resources and generate XML documentation comments. /// diff --git a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs index 94f7e6879ac..e013e996dfc 100644 --- a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs +++ b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs @@ -197,6 +197,7 @@ public DiagnosticInfo FilterDiagnosticInfo(DiagnosticInfo diagnosticInfo, Compil public abstract int ERR_PermissionSetAttributeFileReadError { get; } // PDB writing: + public abstract int ERR_EncodinglessSyntaxTree { get; } public abstract int WRN_PdbUsingNameTooLong { get; } public abstract int WRN_PdbLocalNameTooLong { get; } public abstract int ERR_PdbWritingFailed { get; } diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index 1ac519b6057..ca6e8d93541 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -30,14 +30,16 @@ internal abstract class CommonPEModuleBuilder internal abstract Cci.ITypeReference EncTranslateType(ITypeSymbol type, DiagnosticBag diagnostics); internal abstract Cci.DebugSourceDocument GetSourceDocumentFromIndex(uint index); + internal readonly DebugDocumentsBuilder DebugDocumentsBuilder; internal readonly IEnumerable ManifestResources; internal IEnumerable Win32Resources; internal Cci.ResourceSection Win32ResourceSection; internal Stream SourceLinkStreamOpt; - public CommonPEModuleBuilder(IEnumerable manifestResources) + public CommonPEModuleBuilder(IEnumerable manifestResources, Compilation compilation) { ManifestResources = manifestResources; + DebugDocumentsBuilder = new DebugDocumentsBuilder(compilation.Options.SourceReferenceResolver, compilation.IsCaseSensitive); } } @@ -62,7 +64,6 @@ internal abstract class PEModuleBuilder, string> _normalizedPathsCache = new ConcurrentCache, string>(16); private readonly TokenMap _referencesInILMap = new TokenMap(); private readonly ItemTokenMap _stringsInILMap = new ItemTokenMap(); @@ -78,14 +79,6 @@ internal abstract class PEModuleBuilder _debugDocuments; - public abstract TEmbeddedTypesManager EmbeddedTypesManagerOpt { get; } /// @@ -122,7 +115,7 @@ internal void SetMethodTestData(ConcurrentDictionary( - compilation.IsCaseSensitive ? - StringComparer.Ordinal : - StringComparer.OrdinalIgnoreCase); } internal sealed override void CompilationFinished() @@ -922,7 +911,7 @@ int Cci.IModule.HintNumberOfMethodDefinitions } } - int Cci.IModule.DebugDocumentCount => _debugDocuments.Count; + int Cci.IModule.DebugDocumentCount => DebugDocumentsBuilder.DebugDocumentCount; #endregion @@ -952,49 +941,5 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) } #endregion - - #region Debug Documents - - internal void AddDebugDocument(Cci.DebugSourceDocument document) - { - _debugDocuments.Add(document.Location, document); - } - - internal Cci.DebugSourceDocument TryGetDebugDocument(string path, string basePath) - { - return TryGetDebugDocumentForNormalizedPath(NormalizeDebugDocumentPath(path, basePath)); - } - - internal Cci.DebugSourceDocument TryGetDebugDocumentForNormalizedPath(string normalizedPath) - { - Cci.DebugSourceDocument document; - _debugDocuments.TryGetValue(normalizedPath, out document); - return document; - } - - internal Cci.DebugSourceDocument GetOrAddDebugDocument(string path, string basePath, Func factory) - { - return _debugDocuments.GetOrAdd(NormalizeDebugDocumentPath(path, basePath), factory); - } - - internal string NormalizeDebugDocumentPath(string path, string basePath) - { - var resolver = _compilation.Options.SourceReferenceResolver; - if (resolver == null) - { - return path; - } - - var key = ValueTuple.Create(path, basePath); - string normalizedPath; - if (!_normalizedPathsCache.TryGetValue(key, out normalizedPath)) - { - normalizedPath = resolver.NormalizePath(path, basePath) ?? path; - _normalizedPathsCache.TryAdd(key, normalizedPath); - } - - return normalizedPath; - } - #endregion } } diff --git a/src/Compilers/Core/Portable/Emit/DebugDocumentsBuilder.cs b/src/Compilers/Core/Portable/Emit/DebugDocumentsBuilder.cs new file mode 100644 index 00000000000..dda57e15ff6 --- /dev/null +++ b/src/Compilers/Core/Portable/Emit/DebugDocumentsBuilder.cs @@ -0,0 +1,75 @@ +// 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 Roslyn.Utilities; +using System; +using System.Collections.Concurrent; + +namespace Microsoft.CodeAnalysis.Emit +{ + internal sealed class DebugDocumentsBuilder + { + // This is a map from the document "name" to the document. + // Document "name" is typically a file path like "C:\Abc\Def.cs". However, that is not guaranteed. + // For compatibility reasons the names are treated as case-sensitive in C# and case-insensitive in VB. + // Neither language trims the names, so they are both sensitive to the leading and trailing whitespaces. + // NOTE: We are not considering how filesystem or debuggers do the comparisons, but how native implementations did. + // Deviating from that may result in unexpected warnings or different behavior (possibly without warnings). + private readonly ConcurrentDictionary _debugDocuments; + private readonly ConcurrentCache, string> _normalizedPathsCache; + private readonly SourceReferenceResolver _resolverOpt; + + public DebugDocumentsBuilder(SourceReferenceResolver resolverOpt, bool isDocumentNameCaseSensitive) + { + _resolverOpt = resolverOpt; + + _debugDocuments = new ConcurrentDictionary( + isDocumentNameCaseSensitive ? + StringComparer.Ordinal : + StringComparer.OrdinalIgnoreCase); + + _normalizedPathsCache = new ConcurrentCache, string>(16); + } + + internal int DebugDocumentCount => _debugDocuments.Count; + + internal void AddDebugDocument(Cci.DebugSourceDocument document) + { + _debugDocuments.Add(document.Location, document); + } + + internal Cci.DebugSourceDocument TryGetDebugDocument(string path, string basePath) + { + return TryGetDebugDocumentForNormalizedPath(NormalizeDebugDocumentPath(path, basePath)); + } + + internal Cci.DebugSourceDocument TryGetDebugDocumentForNormalizedPath(string normalizedPath) + { + Cci.DebugSourceDocument document; + _debugDocuments.TryGetValue(normalizedPath, out document); + return document; + } + + internal Cci.DebugSourceDocument GetOrAddDebugDocument(string path, string basePath, Func factory) + { + return _debugDocuments.GetOrAdd(NormalizeDebugDocumentPath(path, basePath), factory); + } + + internal string NormalizeDebugDocumentPath(string path, string basePath) + { + if (_resolverOpt == null) + { + return path; + } + + var key = ValueTuple.Create(path, basePath); + string normalizedPath; + if (!_normalizedPathsCache.TryGetValue(key, out normalizedPath)) + { + normalizedPath = _resolverOpt.NormalizePath(path, basePath) ?? path; + _normalizedPathsCache.TryAdd(key, normalizedPath); + } + + return normalizedPath; + } + } +} diff --git a/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb b/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb index 1fcb340cf7b..534d3201452 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/MethodCompiler.vb @@ -94,7 +94,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic _filterOpt = filter If emittingPdb OrElse moduleBeingBuiltOpt?.EmitOptions.EmitDynamicAnalysisData Then - _debugDocumentProvider = Function(path As String, basePath As String) moduleBeingBuiltOpt.GetOrAddDebugDocument(path, basePath, AddressOf CreateDebugDocumentForFile) + _debugDocumentProvider = Function(path As String, basePath As String) moduleBeingBuiltOpt.DebugDocumentsBuilder.GetOrAddDebugDocument(path, basePath, AddressOf CreateDebugDocumentForFile) End If If compilation.Options.ConcurrentBuild Then diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 25cf8526be4..e0965afcede 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2219,7 +2219,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic SynthesizedMetadataCompiler.ProcessSynthesizedMembers(Me, moduleBeingBuilt, cancellationToken) Else ' start generating PDB checksums if we need to emit PDBs - If emittingPdb AndAlso Not StartSourceChecksumCalculation(moduleBeingBuilt, diagnostics) Then + If emittingPdb AndAlso Not StartSourceChecksumCalculation(moduleBeingBuilt.DebugDocumentsBuilder, diagnostics) Then Return False End If @@ -2286,43 +2286,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return FilterAndAppendAndFreeDiagnostics(diagnostics, xmlDiagnostics) End Function - Private Function StartSourceChecksumCalculation(moduleBeingBuilt As PEModuleBuilder, diagnostics As DiagnosticBag) As Boolean - ' Check that all syntax trees are debuggable - Dim allTreesDebuggable = True - For Each tree In Me.SyntaxTrees - If Not String.IsNullOrEmpty(tree.FilePath) AndAlso tree.GetText().Encoding Is Nothing Then - diagnostics.Add(ERRID.ERR_EncodinglessSyntaxTree, tree.GetRoot().GetLocation()) - allTreesDebuggable = False - End If - Next - - If Not allTreesDebuggable Then - Return False - End If - - ' Add debug documents for all trees with distinct paths. - For Each tree In Me.SyntaxTrees - If Not String.IsNullOrEmpty(tree.FilePath) Then - ' 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. - Dim normalizedPath = moduleBeingBuilt.NormalizeDebugDocumentPath(tree.FilePath, basePath:=Nothing) - Dim existingDoc = moduleBeingBuilt.TryGetDebugDocumentForNormalizedPath(normalizedPath) - If existingDoc Is Nothing Then - moduleBeingBuilt.AddDebugDocument(MakeDebugSourceDocumentForTree(normalizedPath, tree)) - End If - End If - Next - - ' Add debug documents for all directives. - ' If there are clashes with already processed directives, report warnings. - ' If there are clashes with debug documents that came from actual trees, ignore the directive. - For Each tree In Me.SyntaxTrees - AddDebugSourceDocumentsForChecksumDirectives(moduleBeingBuilt, tree, diagnostics) - Next - - Return True - End Function - Private Iterator Function AddedModulesResourceNames(diagnostics As DiagnosticBag) As IEnumerable(Of String) Dim modules As ImmutableArray(Of ModuleSymbol) = SourceAssembly.Modules @@ -2368,8 +2331,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return If(corLibrary Is Nothing, String.Empty, corLibrary.Assembly.ManifestModule.MetadataVersion) End Function - Private Shared Sub AddDebugSourceDocumentsForChecksumDirectives( - moduleBeingBuilt As PEModuleBuilder, + Friend Overrides Sub AddDebugSourceDocumentsForChecksumDirectives( + documentsBuilder As DebugDocumentsBuilder, tree As SyntaxTree, diagnosticBag As DiagnosticBag) @@ -2381,15 +2344,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim path = checksumDirective.ExternalSource.ValueText Dim checkSumText = checksumDirective.Checksum.ValueText - Dim normalizedPath = moduleBeingBuilt.NormalizeDebugDocumentPath(path, basePath:=tree.FilePath) - Dim existingDoc = moduleBeingBuilt.TryGetDebugDocumentForNormalizedPath(normalizedPath) + Dim normalizedPath = documentsBuilder.NormalizeDebugDocumentPath(path, basePath:=tree.FilePath) + Dim existingDoc = documentsBuilder.TryGetDebugDocumentForNormalizedPath(normalizedPath) If existingDoc IsNot Nothing Then ' directive matches a file path on an actual tree. ' Dev12 compiler just ignores the directive 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) Then + If existingDoc.IsComputedChecksum Then Continue For End If @@ -2412,7 +2375,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic MakeCheckSumBytes(checksumDirective.Checksum.ValueText), Guid.Parse(checksumDirective.Guid.ValueText)) - moduleBeingBuilt.AddDebugDocument(newDocument) + documentsBuilder.AddDebugDocument(newDocument) End If Next End Sub @@ -2449,12 +2412,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return builder.ToImmutableAndFree() End Function - Private Shared Function MakeDebugSourceDocumentForTree(normalizedPath As String, tree As SyntaxTree) As DebugSourceDocument - Return New DebugSourceDocument(normalizedPath, DebugSourceDocument.CorSymLanguageTypeBasic, Function() tree.GetChecksumAndAlgorithm()) - End Function + Friend Overrides ReadOnly Property DebugSourceDocumentLanguageId As Guid + Get + Return DebugSourceDocument.CorSymLanguageTypeBasic + End Get + End Property Friend Overrides Function HasCodeToEmit() As Boolean - ' TODO (tomat): For Each syntaxTree In SyntaxTrees Dim unit = syntaxTree.GetCompilationUnitRoot() If unit.Members.Count > 0 Then diff --git a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb index 2e10ec677ad..ce2bfdd8505 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb @@ -274,7 +274,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Private Sub AddSymbolLocation(result As MultiDictionary(Of Cci.DebugSourceDocument, Cci.DefinitionWithLocation), location As Location, definition As Cci.IDefinition) Dim span As FileLinePositionSpan = location.GetLineSpan() - Dim doc As Cci.DebugSourceDocument = Me.TryGetDebugDocument(span.Path, basePath:=location.SourceTree.FilePath) + Dim doc As Cci.DebugSourceDocument = DebugDocumentsBuilder.TryGetDebugDocument(span.Path, basePath:=location.SourceTree.FilePath) If (doc IsNot Nothing) Then result.Add(doc, New Cci.DefinitionWithLocation( diff --git a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb index 65a76d22e66..5fef82f91cb 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb @@ -456,6 +456,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Sub ' PDB Writer + Public Overrides ReadOnly Property ERR_EncodinglessSyntaxTree As Integer + Get + Return ERRID.ERR_EncodinglessSyntaxTree + End Get + End Property + Public Overrides ReadOnly Property WRN_PdbUsingNameTooLong As Integer Get Return ERRID.WRN_PdbUsingNameTooLong diff --git a/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs b/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs index 5425a877847..9e1cff85b3a 100644 --- a/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs +++ b/src/Test/Utilities/Shared/Mocks/TestMessageProvider.cs @@ -29,6 +29,11 @@ public override ReportDiagnostic GetDiagnosticReport(DiagnosticInfo diagnosticIn throw new NotImplementedException(); } + public override int ERR_EncodinglessSyntaxTree + { + get { throw new NotImplementedException(); } + } + public override int ERR_InvalidPathMap { get { throw new NotImplementedException(); } -- GitLab