diff --git a/build.sh b/build.sh index fd659a3dd15ea21c01b318c172753d50fcb7678f..e827a9d5ccb10af2b23802fbdde57ccf364ce0b3 100755 --- a/build.sh +++ b/build.sh @@ -122,6 +122,13 @@ then build_args+=" /p:BootstrapBuildPath=${bootstrap_path}" fi +# https://github.com/dotnet/roslyn/issues/23736 +UNAME="$(uname)" +if [[ "$UNAME" == "Darwin" ]] +then + build_args+=" /p:UseRoslynAnalyzers=false" +fi + if [[ "${build}" == true ]] then echo "Building Compilers.sln" diff --git a/build/Targets/Versions.props b/build/Targets/Versions.props index 795be0095393dae30672bb15fbdd0c4483c1cb61..70f759eb54dc6018635ba15577f8c0bb278ca358 100644 --- a/build/Targets/Versions.props +++ b/build/Targets/Versions.props @@ -14,7 +14,7 @@ dev - beta2 + beta3 $(RoslynFileVersionBase)-$(RoslynNuGetMoniker) diff --git a/build/config/PublishData.json b/build/config/PublishData.json index 1a99d2061e819de3eccd98fa47e5afe14182374e..a82232cf4c70789976c16f497f70f2d9994a5436 100644 --- a/build/config/PublishData.json +++ b/build/config/PublishData.json @@ -5,7 +5,14 @@ "version": "2.7.*", "nuget": [ "https://dotnet.myget.org/F/roslyn/api/v2/package" ], "vsix": [ "https://dotnet.myget.org/F/roslyn/vsix/upload" ], - "channels": [ "dev15.6" ] + "channels": [ "dev15.6", "dev15.6p3" ] + }, + "dev15.6-preview2-vs-deps": { + "nugetKind": "PerBuildPreRelease", + "version": "2.7.*", + "nuget": [ "https://dotnet.myget.org/F/roslyn/api/v2/package" ], + "vsix": [ "https://dotnet.myget.org/F/roslyn/vsix/upload" ], + "channels": [ "dev15.6p2" ] }, "dev15.5.x": { "nugetKind": "PerBuildPreRelease", diff --git a/build/scripts/tests.sh b/build/scripts/tests.sh index c37af5f1c1dabbb850c4542bf55a2fb29d23c881..9dc645094b1d585fa744665006143a5c10b028df 100755 --- a/build/scripts/tests.sh +++ b/build/scripts/tests.sh @@ -45,8 +45,8 @@ do runtimeconfig_json="${file_name%.*}".runtimeconfig.json # If the user specifies a test on the command line, only run that one - # "${2:-}" => take second arg, empty string if unset (regex always matches if empty) - if [[ ! "${file_name}" =~ "${2:-}" ]] + # "${2:-}" => take second arg, empty string if unset + if [[ ("${2:-}" != "") && (! "${file_name}" =~ "${2:-}") ]] then echo "Skipping ${file_name}" continue diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index 276e2de3335668542e0fdaf5d8ca76d8ab908b8b..46c6af1a94164da8bc3862f2ecfbeaf75b52a80a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -305,14 +305,14 @@ internal virtual ImportChain ImportChain } /// - /// Get that can be used to quickly - /// check for TypeIdentifier attribute applications in context of this binder. + /// Get that can be used to quickly + /// check for certain attribute applications in context of this binder. /// - internal virtual QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker + internal virtual QuickAttributeChecker QuickAttributeChecker { get { - return _next.QuickTypeIdentifierAttributeChecker; + return _next.QuickAttributeChecker; } } diff --git a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs index 028f4596341921d3cc62d381809b0ca8e86ed84b..15012a853822ed12ce50676355ef55b73a359433 100644 --- a/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/BuckStopsHereBinder.cs @@ -28,14 +28,14 @@ internal override ImportChain ImportChain } /// - /// Get that can be used to quickly - /// check for TypeIdentifier attribute applications in context of this binder. + /// Get that can be used to quickly + /// check for certain attribute applications in context of this binder. /// - internal override QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker + internal override QuickAttributeChecker QuickAttributeChecker { get { - return QuickTypeIdentifierAttributeChecker.Predefined; + return QuickAttributeChecker.Predefined; } } diff --git a/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs b/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs index 104ccc68d4d7f2aa5e7f67fede78bf004be3ec4a..e9b032db42d0c76b5665a72e54e361495dca2220 100644 --- a/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs @@ -21,7 +21,8 @@ internal sealed class InContainerBinder : Binder private readonly Func, Imports> _computeImports; private Imports _lazyImports; private ImportChain _lazyImportChain; - private QuickTypeIdentifierAttributeChecker _lazyQuickTypeIdentifierAttributeChecker; + private QuickAttributeChecker _lazyQuickAttributeChecker; + private readonly SyntaxList _usingsSyntax; /// /// Creates a binder for a container with imports (usings and extern aliases) that can be @@ -35,6 +36,20 @@ internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, CSharpS _container = container; _computeImports = basesBeingResolved => Imports.FromSyntax(declarationSyntax, this, basesBeingResolved, inUsing); + + if (!inUsing) + { + if (declarationSyntax.Kind() == SyntaxKind.CompilationUnit) + { + var compilationUnit = (CompilationUnitSyntax)declarationSyntax; + _usingsSyntax = compilationUnit.Usings; + } + else if (declarationSyntax.Kind() == SyntaxKind.NamespaceDeclaration) + { + var namespaceDecl = (NamespaceDeclarationSyntax)declarationSyntax; + _usingsSyntax = namespaceDecl.Usings; + } + } } /// @@ -103,26 +118,26 @@ internal override ImportChain ImportChain } /// - /// Get that can be used to quickly - /// check for TypeIdentifier attribute applications in context of this binder. + /// Get that can be used to quickly + /// check for certain attribute applications in context of this binder. /// - internal override QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker + internal override QuickAttributeChecker QuickAttributeChecker { get { - if (_lazyQuickTypeIdentifierAttributeChecker == null) + if (_lazyQuickAttributeChecker == null) { - QuickTypeIdentifierAttributeChecker result = this.Next.QuickTypeIdentifierAttributeChecker; + QuickAttributeChecker result = this.Next.QuickAttributeChecker; if ((object)_container == null || _container.Kind == SymbolKind.Namespace) { - result = result.AddAliasesIfAny(GetImports(basesBeingResolved: null).UsingAliases); + result = result.AddAliasesIfAny(_usingsSyntax); } - _lazyQuickTypeIdentifierAttributeChecker = result; + _lazyQuickAttributeChecker = result; } - return _lazyQuickTypeIdentifierAttributeChecker; + return _lazyQuickAttributeChecker; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/QuickAttributeChecker.cs b/src/Compilers/CSharp/Portable/Symbols/Source/QuickAttributeChecker.cs new file mode 100644 index 0000000000000000000000000000000000000000..2377262444f15d917e15d18fce05c68a13d3b390 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/QuickAttributeChecker.cs @@ -0,0 +1,139 @@ +// 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.Generic; +using System.Diagnostics; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// The QuickAttributeChecker applies a simple fast heuristic for determining probable + /// attributes of certain kinds without binding attribute types, just by looking at the final syntax of an + /// attribute usage. + /// + /// + /// It works by maintaining a dictionary of all possible simple names that might map to the given + /// attribute. + /// + internal sealed class QuickAttributeChecker + { + private readonly Dictionary _nameToAttributeMap; + private static QuickAttributeChecker _lazyPredefinedQuickAttributeChecker; + +#if DEBUG + private bool _sealed; +#endif + + internal static QuickAttributeChecker Predefined + { + get + { + if (_lazyPredefinedQuickAttributeChecker is null) + { + Interlocked.CompareExchange(ref _lazyPredefinedQuickAttributeChecker, CreatePredefinedQuickAttributeChecker(), null); + } + + return _lazyPredefinedQuickAttributeChecker; + } + } + + private static QuickAttributeChecker CreatePredefinedQuickAttributeChecker() + { + var result = new QuickAttributeChecker(); + result.AddName(AttributeDescription.TypeIdentifierAttribute.Name, QuickAttributes.TypeIdentifier); + result.AddName(AttributeDescription.TypeForwardedToAttribute.Name, QuickAttributes.TypeForwardedTo); + +#if DEBUG + result._sealed = true; +#endif + return result; + } + + private QuickAttributeChecker() + { + _nameToAttributeMap = new Dictionary(StringComparer.Ordinal); + // NOTE: caller must seal + } + + private QuickAttributeChecker(QuickAttributeChecker previous) + { + _nameToAttributeMap = new Dictionary(previous._nameToAttributeMap, StringComparer.Ordinal); + // NOTE: caller must seal + } + + private void AddName(string name, QuickAttributes newAttributes) + { +#if DEBUG + Debug.Assert(!_sealed); +#endif + var currentValue = QuickAttributes.None; + _nameToAttributeMap.TryGetValue(name, out currentValue); + + QuickAttributes newValue = newAttributes | currentValue; + _nameToAttributeMap[name] = newValue; + } + + internal QuickAttributeChecker AddAliasesIfAny(SyntaxList usingsSyntax) + { + if (usingsSyntax.Count == 0) + { + return this; + } + + QuickAttributeChecker newChecker = null; + + foreach (var usingDirective in usingsSyntax) + { + if (usingDirective.Alias != null) + { + string name = usingDirective.Alias.Name.Identifier.ValueText; + string target = usingDirective.Name.GetUnqualifiedName().Identifier.ValueText; + + if (_nameToAttributeMap.TryGetValue(target, out var foundAttributes)) + { + // copy the QuickAttributes from alias target to alias name + (newChecker ?? (newChecker = new QuickAttributeChecker(this))).AddName(name, foundAttributes); + } + } + } + + if (newChecker != null) + { +#if DEBUG + newChecker._sealed = true; +#endif + return newChecker; + } + + return this; + } + + public bool IsPossibleMatch(AttributeSyntax attr, QuickAttributes pattern) + { +#if DEBUG + Debug.Assert(_sealed); +#endif + string name = attr.Name.GetUnqualifiedName().Identifier.ValueText; + QuickAttributes foundAttributes; + + // We allow "Name" to bind to "NameAttribute" + if (_nameToAttributeMap.TryGetValue(name, out foundAttributes) || + _nameToAttributeMap.TryGetValue(name + "Attribute", out foundAttributes)) + { + return (foundAttributes & pattern) != 0; + } + + return false; + } + } + + [Flags] + internal enum QuickAttributes : byte + { + None = 0, + TypeIdentifier = 1 << 0, + TypeForwardedTo = 2 << 0 + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/QuickTypeIdentifierAttributeChecker.cs b/src/Compilers/CSharp/Portable/Symbols/Source/QuickTypeIdentifierAttributeChecker.cs deleted file mode 100644 index 1cb49c8bbe2355274f97dce2ca87f588bdb8fd4d..0000000000000000000000000000000000000000 --- a/src/Compilers/CSharp/Portable/Symbols/Source/QuickTypeIdentifierAttributeChecker.cs +++ /dev/null @@ -1,82 +0,0 @@ -// 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.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.CodeAnalysis.CSharp.Symbols -{ - /// - /// The QuickTypeIdentifierAttributeChecker applies a simple fast heuristic for determining probable - /// TypeIdentifier attributes without binding attribute types, just by looking at the final syntax of an - /// attribute usage. It is accessed via the QuickTypeIdentifierAttributeChecker property on Binder. - /// - /// - /// It works by maintaining a dictionary of all possible simple names that might map to a TypeIdentifier - /// attribute. - /// - internal class QuickTypeIdentifierAttributeChecker - { - private readonly HashSet _candidates; -#if DEBUG - private bool _sealed; -#endif - - public static readonly QuickTypeIdentifierAttributeChecker Predefined = new QuickTypeIdentifierAttributeChecker(); - - private QuickTypeIdentifierAttributeChecker() - { - _candidates = new HashSet(); - _candidates.Add(AttributeDescription.TypeIdentifierAttribute.Name); -#if DEBUG - _sealed = true; -#endif - } - - private QuickTypeIdentifierAttributeChecker(QuickTypeIdentifierAttributeChecker previous) - { - _candidates = new HashSet(previous._candidates); - } - - private void AddCandidate(string candidate) - { -#if DEBUG - Debug.Assert(!_sealed); -#endif - _candidates.Add(candidate); - } - - public QuickTypeIdentifierAttributeChecker AddAliasesIfAny(ImmutableDictionary usingAliases) - { - QuickTypeIdentifierAttributeChecker newChecker = null; - - foreach (KeyValuePair pair in usingAliases) - { - if (_candidates.Contains(pair.Value.UsingDirective.Name.GetUnqualifiedName().Identifier.ValueText)) - { - (newChecker ?? (newChecker = new QuickTypeIdentifierAttributeChecker(this))).AddCandidate(pair.Key); - } - } - - if (newChecker != null) - { -#if DEBUG - newChecker._sealed = true; -#endif - return newChecker; - } - - return this; - } - - public bool IsPossibleMatch(AttributeSyntax attr) - { -#if DEBUG - Debug.Assert(_sealed); -#endif - string name = attr.Name.GetUnqualifiedName().Identifier.ValueText; - return _candidates.Contains(name) || _candidates.Contains(name + "Attribute"); - } - } -} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs index bf50bfba4ff0ff9e1609cb016bf2b4cb4f37ca3d..5844d04ee6b64a04d861819e46f3262703ac6314 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs @@ -1556,6 +1556,33 @@ internal CommonAssemblyWellKnownAttributeData GetSourceDecodedWellKnownAttribute return (CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData; } + /// + /// This only forces binding of attributes that look like they may be forwarded types attributes (syntactically). + /// + internal HashSet GetForwardedTypes() + { + CustomAttributesBag attributesBag = _lazySourceAttributesBag; + if (attributesBag?.IsDecodedWellKnownAttributeDataComputed == true) + { + // Use already decoded attributes + return ((CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData)?.ForwardedTypes; + } + + attributesBag = null; + LoadAndValidateAttributes(OneOrMany.Create(GetAttributeDeclarations()), ref attributesBag, attributeMatchesOpt: this.IsPossibleForwardedTypesAttribute); + + var wellKnownAttributeData = (CommonAssemblyWellKnownAttributeData)attributesBag?.DecodedWellKnownAttributeData; + return wellKnownAttributeData?.ForwardedTypes; + } + + private bool IsPossibleForwardedTypesAttribute(AttributeSyntax node) + { + QuickAttributeChecker checker = + this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker; + + return checker.IsPossibleMatch(node, QuickAttributes.TypeForwardedTo); + } + private static IEnumerable GetSecurityAttributes(CustomAttributesBag attributesBag) { Debug.Assert(attributesBag.IsSealed); @@ -2556,13 +2583,14 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti if (_lazyForwardedTypesFromSource == null) { IDictionary forwardedTypesFromSource; - CommonAssemblyWellKnownAttributeData wellKnownAttributeData = GetSourceDecodedWellKnownAttributeData(); + // Get the TypeForwardedTo attributes with minimal binding to avoid cycle problems + HashSet forwardedTypes = GetForwardedTypes(); - if (wellKnownAttributeData != null && wellKnownAttributeData.ForwardedTypes != null) + if (forwardedTypes != null) { forwardedTypesFromSource = new Dictionary(StringOrdinalComparer.Instance); - foreach (NamedTypeSymbol forwardedType in wellKnownAttributeData.ForwardedTypes) + foreach (NamedTypeSymbol forwardedType in forwardedTypes) { NamedTypeSymbol originalDefinition = forwardedType.OriginalDefinition; Debug.Assert((object)originalDefinition.ContainingType == null, "How did a nested type get forwarded?"); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index de5c0fe47574813ff63f44ca98a9d820f50e1cae..8de5fc066cdcb6d50cc0ee4f00f3b964337e3f81 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -780,13 +780,13 @@ private void CheckPresenceOfTypeIdentifierAttribute() foreach (SyntaxList list in attributeLists) { var syntaxTree = list.Node.SyntaxTree; - QuickTypeIdentifierAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(list.Node.SyntaxTree).GetBinder(list.Node).QuickTypeIdentifierAttributeChecker; + QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(list.Node.SyntaxTree).GetBinder(list.Node).QuickAttributeChecker; foreach (AttributeListSyntax attrList in list) { foreach (AttributeSyntax attr in attrList.Attributes) { - if (checker.IsPossibleMatch(attr)) + if (checker.IsPossibleMatch(attr, QuickAttributes.TypeIdentifier)) { // This attribute syntax might be an application of TypeIdentifierAttribute. // Let's bind it. diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs index baca5d8c6928c8410869329403a2be9d02b9726f..223d85128e59dc3c6fb1aca960842b48742631fb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs @@ -255,19 +255,21 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArraySpecific part of the symbol to which the attributes apply, or if the attributes apply to the symbol itself. /// Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed. /// Binder to use. If null, GetBinderFactory will be used. + /// If specified, only load attributes that match this predicate, and any diagnostics produced will be dropped. /// Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true. internal bool LoadAndValidateAttributes( OneOrMany> attributesSyntaxLists, ref CustomAttributesBag lazyCustomAttributesBag, AttributeLocation symbolPart = AttributeLocation.None, bool earlyDecodingOnly = false, - Binder binderOpt = null) + Binder binderOpt = null, + Func attributeMatchesOpt = null) { var diagnostics = DiagnosticBag.GetInstance(); var compilation = this.DeclaringCompilation; ImmutableArray binders; - ImmutableArray attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, binderOpt, out binders); + ImmutableArray attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, attributeMatchesOpt, binderOpt, out binders); Debug.Assert(!attributesToBind.IsDefault); ImmutableArray boundAttributes; @@ -344,8 +346,11 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray.Empty; } @@ -382,6 +387,7 @@ private void RecordPresenceOfBadAttributes(ImmutableArray b AttributeLocation symbolPart, DiagnosticBag diagnostics, CSharpCompilation compilation, + Func attributeMatchesOpt, Binder rootBinderOpt, out ImmutableArray binders) { @@ -409,8 +415,22 @@ private void RecordPresenceOfBadAttributes(ImmutableArray b } var attributesToBind = attributeDeclarationSyntax.Attributes; - syntaxBuilder.AddRange(attributesToBind); - attributesToBindCount += attributesToBind.Count; + if (attributeMatchesOpt is null) + { + syntaxBuilder.AddRange(attributesToBind); + attributesToBindCount += attributesToBind.Count; + } + else + { + foreach (var attribute in attributesToBind) + { + if (attributeMatchesOpt(attribute)) + { + syntaxBuilder.Add(attribute); + attributesToBindCount++; + } + } + } } } diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 068ee4d42b4357f50c1b4a2ad3c8ed7673d587dc..b4ac9d40a3b5aeea2808e83e679d9cf5d3e10422 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -4368,6 +4368,24 @@ public void PublicSign_KeyFileRelativePath() Assert.Equal(Path.Combine(_baseDirectory, "test.snk"), parsedArgs.CompilationOptions.CryptoKeyFile); } + [Fact] + [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")] + public void PublicSignWithEmptyKeyPath() + { + DefaultParse(new[] { "/publicsign", "/keyfile:", "a.cs" }, _baseDirectory).Errors.Verify( + // error CS2005: Missing file specification for 'keyfile' option + Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile").WithLocation(1, 1)); + } + + [Fact] + [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")] + public void PublicSignWithEmptyKeyPath2() + { + DefaultParse(new[] { "/publicsign", "/keyfile:\"\"", "a.cs" }, _baseDirectory).Errors.Verify( + // error CS2005: Missing file specification for 'keyfile' option + Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile").WithLocation(1, 1)); + } + [WorkItem(546301, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546301")] [Fact] public void SubsystemVersionTests() diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs index 7326078254e03711843766ece0ddd7e1ae185b68..061a49009050119fa4279e7d7f90fb2ac7c64a27 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs @@ -25,6 +25,426 @@ public class AttributeTests : WellKnownAttributesTestBase #region Function Tests + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void TestQuickAttributeChecker() + { + var predefined = QuickAttributeChecker.Predefined; + var typeForwardedTo = SyntaxFactory.Attribute(SyntaxFactory.ParseName("TypeForwardedTo")); + var typeIdentifier = SyntaxFactory.Attribute(SyntaxFactory.ParseName("TypeIdentifier")); + + Assert.True(predefined.IsPossibleMatch(typeForwardedTo, QuickAttributes.TypeForwardedTo)); + Assert.False(predefined.IsPossibleMatch(typeForwardedTo, QuickAttributes.TypeIdentifier)); + Assert.False(predefined.IsPossibleMatch(typeForwardedTo, QuickAttributes.None)); + + Assert.False(predefined.IsPossibleMatch(typeIdentifier, QuickAttributes.TypeForwardedTo)); + Assert.True(predefined.IsPossibleMatch(typeIdentifier, QuickAttributes.TypeIdentifier)); + Assert.False(predefined.IsPossibleMatch(typeIdentifier, QuickAttributes.None)); + + var alias1 = SyntaxFactory.Attribute(SyntaxFactory.ParseName("alias1")); + var checker1 = WithAliases(predefined, "using alias1 = TypeForwardedToAttribute;"); + Assert.True(checker1.IsPossibleMatch(alias1, QuickAttributes.TypeForwardedTo)); + Assert.False(checker1.IsPossibleMatch(alias1, QuickAttributes.TypeIdentifier)); + + var checker1a = WithAliases(checker1, "using alias1 = TypeIdentifierAttribute;"); + Assert.True(checker1a.IsPossibleMatch(alias1, QuickAttributes.TypeForwardedTo)); + Assert.True(checker1a.IsPossibleMatch(alias1, QuickAttributes.TypeIdentifier)); + + var checker1b = WithAliases(checker1, "using alias2 = TypeIdentifierAttribute;"); + var alias2 = SyntaxFactory.Attribute(SyntaxFactory.ParseName("alias2")); + Assert.True(checker1b.IsPossibleMatch(alias1, QuickAttributes.TypeForwardedTo)); + Assert.False(checker1b.IsPossibleMatch(alias1, QuickAttributes.TypeIdentifier)); + Assert.False(checker1b.IsPossibleMatch(alias2, QuickAttributes.TypeForwardedTo)); + Assert.True(checker1b.IsPossibleMatch(alias2, QuickAttributes.TypeIdentifier)); + + var checker3 = WithAliases(predefined, "using alias3 = TypeForwardedToAttribute; using alias3 = TypeIdentifierAttribute;"); + var alias3 = SyntaxFactory.Attribute(SyntaxFactory.ParseName("alias3")); + Assert.True(checker3.IsPossibleMatch(alias3, QuickAttributes.TypeForwardedTo)); + Assert.True(checker3.IsPossibleMatch(alias3, QuickAttributes.TypeIdentifier)); + + QuickAttributeChecker WithAliases(QuickAttributeChecker checker, string aliases) + { + var nodes = Parse(aliases).GetRoot().DescendantNodes().OfType(); + var list = new SyntaxList().AddRange(nodes); + return checker.AddAliasesIfAny(list); + } + } + + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithMissingType_WithIrrelevantType() + { + var origLib_cs = @"public class C { }"; + + var newLib_cs = @" +[assembly: RefersToLib] // to bind this, we'll want to lookup type C in 'lib' (during overload resolution of attribute constructors) albeit irrelevant +// but C won't exist +"; + + var reference_cs = +@"using System; +public class RefersToLibAttribute : Attribute +{ + public RefersToLibAttribute() { } + public RefersToLibAttribute(C c) { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics(); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithDiagnostic() + { + var origLib_cs = @"public class C { }"; + + var newLib_cs = @" +[assembly: RefersToLib] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(1)] +"; + + var reference_cs = +@"using System; +public class RefersToLibAttribute : Attribute +{ + public RefersToLibAttribute() { } + public RefersToLibAttribute(C c) { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics( + // (3,60): error CS1503: Argument 1: cannot convert from 'int' to 'System.Type' + // [assembly: System.Runtime.CompilerServices.TypeForwardedTo(1)] + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "System.Type").WithLocation(3, 60) + ); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics( + // (3,60): error CS1503: Argument 1: cannot convert from 'int' to 'System.Type' + // [assembly: System.Runtime.CompilerServices.TypeForwardedTo(1)] + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "System.Type").WithLocation(3, 60) + ); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithMissingType_WithRelevantType() + { + var origLib_cs = @"public class C : System.Attribute { }"; + + var newLib_cs = @" +[assembly: RefersToLib] // to bind this, we'll need to find type C in 'lib' +// but C won't exist +"; + + var reference_cs = +@" +public class RefersToLibAttribute : C +{ + public RefersToLibAttribute() { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics( + // (2,12): error CS7068: Reference to type 'C' claims it is defined in this assembly, but it is not defined in source or any added modules + // [assembly: RefersToLib] // to bind this, we'll want to lookup type C in 'lib' + Diagnostic(ErrorCode.ERR_MissingTypeInSource, "RefersToLib").WithArguments("C").WithLocation(2, 12) + ); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics( + // (2,12): error CS7068: Reference to type 'C' claims it is defined in this assembly, but it is not defined in source or any added modules + // [assembly: RefersToLib] // to bind this, we'll want to lookup type C in 'lib' + Diagnostic(ErrorCode.ERR_MissingTypeInSource, "RefersToLib").WithArguments("C").WithLocation(2, 12) + ); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithStaticUsing() + { + var origLib_cs = @"public class C : System.Attribute { }"; + + var newLib_cs = @" +using static RefersToLibAttribute; + // Binding this will cause a lookup for 'C' in 'lib'. + // Such lookup requires binding 'TypeForwardedTo' attributes (and aliases), but we should not bind other usings, to avoid an infinite recursion. + +[assembly: System.Reflection.AssemblyTitleAttribute(""title"")] + +public class Ignore +{ + public void UseStaticUsing() { Method(); } +} +"; + + var reference_cs = +@" +public class RefersToLibAttribute : C +{ + public RefersToLibAttribute() { } + public static void Method() { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics(); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithTypeForward_WithIrrelevantType() + { + var origLib_cs = @"public class C { }"; + + var newLib_cs = @" +[assembly: RefersToLib] // to bind this, we'll want to lookup type C in 'lib' (during overload resolution of attribute constructors) albeit irrelevant +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C))] // but C is forwarded +"; + + var reference_cs = +@"using System; +public class RefersToLibAttribute : Attribute +{ + public RefersToLibAttribute() { } + public RefersToLibAttribute(C c) { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var newComp = CreateStandardCompilation(origLib_cs, assemblyName: "new"); + newComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, + references: new[] { compWithReferenceToLib.EmitToImageReference(), newComp.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics(); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, + references: new[] { compWithReferenceToLib.ToMetadataReference(), newComp.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithTypeForward_WithRelevantType() + { + var origLib_cs = @"public class C : System.Attribute { }"; + + var newLib_cs = @" +[assembly: RefersToLib] // to bind this, we'll need to find type C in 'lib' +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C))] // but C is forwarded +"; + + var reference_cs = +@" +public class RefersToLibAttribute : C +{ + public RefersToLibAttribute() { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var newComp = CreateStandardCompilation(origLib_cs, assemblyName: "new"); + newComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference(), newComp.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics(); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.ToMetadataReference(), newComp.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics(); + + var newLibComp3 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference(), newComp.EmitToImageReference() }, assemblyName: "lib"); + newLibComp3.SourceAssembly.GetAttributes(); + newLibComp3.VerifyDiagnostics(); + } + + /// + /// Looking up C explicitly after calling GetAttributes will cause to use the cached attributes, rather that do partial binding + /// + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithCheckAttributes() + { + var cDefinition_cs = @"public class C { }"; + var derivedDefinition_cs = @"public class Derived : C { }"; + + var typeForward_cs = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C))] +"; + + var origLibComp = CreateStandardCompilation(cDefinition_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var compWithDerivedAndReferenceToLib = CreateStandardCompilation(typeForward_cs + derivedDefinition_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithDerivedAndReferenceToLib .VerifyDiagnostics(); + + var compWithC = CreateStandardCompilation(cDefinition_cs, assemblyName: "new"); + compWithC.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(typeForward_cs, references: new[] { compWithDerivedAndReferenceToLib.EmitToImageReference(), compWithC.EmitToImageReference() }, assemblyName: "lib"); + var attribute = newLibComp.SourceAssembly.GetAttributes().Single(); // GetAttributes binds all attributes + Assert.Equal("System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(C))", attribute.ToString()); + + var derived = (NamedTypeSymbol)newLibComp.GetMember("Derived"); + var c = derived.BaseType; // get C + Assert.Equal("C", c.ToTestDisplayString()); + Assert.False(c.IsErrorType()); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithTypeForward_WithRelevantType_WithAlias() + { + var origLib_cs = @"public class C : System.Attribute { }"; + + var newLib_cs = @" +using alias1 = System.Runtime.CompilerServices.TypeForwardedToAttribute; +[assembly: RefersToLib] // to bind this, we'll need to find type C in 'lib' +[assembly: alias1(typeof(C))] // but C is forwarded via alias +"; + + var reference_cs = +@" +public class RefersToLibAttribute : C +{ + public RefersToLibAttribute() { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var newComp = CreateStandardCompilation(origLib_cs, assemblyName: "new"); + newComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, + references: new[] { compWithReferenceToLib.EmitToImageReference(), newComp.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics(); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, + references: new[] { compWithReferenceToLib.ToMetadataReference(), newComp.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithExistingType_WithIrrelevantType() + { + var origLib_cs = @"public class C { }"; + + var newLib_cs = @" +[assembly: RefersToLib] // to bind this, we'll want to lookup type C in 'lib' (during overload resolution of attribute constructors) albeit irrelevant +public class C { } // and C exists here +"; + + var reference_cs = +@"using System; +public class RefersToLibAttribute : Attribute +{ + public RefersToLibAttribute() { } + public RefersToLibAttribute(C c) { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var newComp = CreateStandardCompilation(origLib_cs, assemblyName: "new"); + newComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference(), newComp.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics(); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.ToMetadataReference(), newComp.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(21194, "https://github.com/dotnet/roslyn/issues/21194")] + public void AttributeWithTypeReferenceToCurrentCompilation_WithExistingType_WithRelevantType() + { + var origLib_cs = @"public class C : System.Attribute { }"; + + var newLib_cs = @" +[assembly: RefersToLib] // to bind this, we'll need to find type C in 'lib' +public class C : System.Attribute { } // and C exists here +"; + + var reference_cs = +@" +public class RefersToLibAttribute : C +{ + public RefersToLibAttribute() { } +} +"; + + var origLibComp = CreateStandardCompilation(origLib_cs, assemblyName: "lib"); + origLibComp.VerifyDiagnostics(); + + var newComp = CreateStandardCompilation(origLib_cs, assemblyName: "new"); + newComp.VerifyDiagnostics(); + + var compWithReferenceToLib = CreateStandardCompilation(reference_cs, references: new[] { origLibComp.EmitToImageReference() }); + compWithReferenceToLib.VerifyDiagnostics(); + + var newLibComp = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.EmitToImageReference(), newComp.EmitToImageReference() }, assemblyName: "lib"); + newLibComp.VerifyDiagnostics(); + + var newLibComp2 = CreateStandardCompilation(newLib_cs, references: new[] { compWithReferenceToLib.ToMetadataReference(), newComp.ToMetadataReference() }, assemblyName: "lib"); + newLibComp2.VerifyDiagnostics(); + } + [Fact] public void TestAssemblyAttributes() { diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs index 41d2f65dbe6c172c3a6fd661a2d5371e99284a2a..bfbc95a09b9e849bfb5ca9dbc0a7adefe08b1797 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/CompilationAPITests.cs @@ -45,6 +45,26 @@ public void PublicSignWithRelativeKeyPath() ); } + [Fact] + [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")] + public void PublicSignWithEmptyKeyPath() + { + CreateStandardCompilation("", options: TestOptions.ReleaseDll.WithPublicSign(true).WithCryptoKeyFile("")).VerifyDiagnostics( + // error CS8102: Public signing was specified and requires a public key, but no public key was specified. + Diagnostic(ErrorCode.ERR_PublicSignButNoKey).WithLocation(1, 1)); + } + + [Fact] + [WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")] + public void PublicSignWithEmptyKeyPath2() + { + CreateStandardCompilation("", options: TestOptions.ReleaseDll.WithPublicSign(true).WithCryptoKeyFile("\"\"")).VerifyDiagnostics( + // error CS8106: Option 'CryptoKeyFile' must be an absolute path. + Diagnostic(ErrorCode.ERR_OptionMustBeAbsolutePath).WithArguments("CryptoKeyFile").WithLocation(1, 1), + // error CS8102: Public signing was specified and requires a public key, but no public key was specified. + Diagnostic(ErrorCode.ERR_PublicSignButNoKey).WithLocation(1, 1)); + } + [Fact] [WorkItem(233669, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=233669")] public void CompilationName() diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index bd6ffbe268497f8d0674680ba9336939b35f50a9..cfc1276dc2dbfb1f1f985e71a2b9e282124a6791 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -8374,7 +8374,6 @@ End Class Assert.False(args.CompilationOptions.PublicSign) End Sub - Public Sub PublicSign_KeyFileRelativePath() @@ -8383,6 +8382,20 @@ End Class parsedArgs.Errors.Verify() End Sub + + + Public Sub PublicSignWithEmptyKeyPath() + Dim parsedArgs = FullParse("/publicsign /keyfile: a.cs", _baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("keyfile", ":").WithLocation(1, 1)) + End Sub + + + + Public Sub PublicSignWithEmptyKeyPath2() + Dim parsedArgs = FullParse("/publicsign /keyfile:"""" a.cs", _baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("keyfile", ":").WithLocation(1, 1)) + End Sub + Public Sub CommandLineMisc() Dim args As VisualBasicCommandLineArguments diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb index 872d19825ca636c0fb6e3285a55e2b087d5c2a7c..57e576684048394e74b35cacec2149b7bfc5fca0 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb @@ -34,6 +34,29 @@ BC37257: Option 'CryptoKeyFile' must be an absolute path. ) End Sub + + + Public Sub PublicSignWithEmptyKeyPath() + Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary). + WithPublicSign(True).WithCryptoKeyFile("") + AssertTheseDiagnostics(VisualBasicCompilation.Create("test", options:=options), + +BC37254: Public sign was specified and requires a public key, but no public key was specified +) + End Sub + + + + Public Sub PublicSignWithEmptyKeyPath2() + Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary). + WithPublicSign(True).WithCryptoKeyFile("""""") + AssertTheseDiagnostics(VisualBasicCompilation.Create("test", options:=options), + +BC37254: Public sign was specified and requires a public key, but no public key was specified +BC37257: Option 'CryptoKeyFile' must be an absolute path. +) + End Sub + Public Sub LocalizableErrorArgumentToStringDoesntStackOverflow() ' Error ID is arbitrary diff --git a/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj b/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj index 0c3f3b985432fe6e0982420f6fa188bc258c65f5..e8971d13ee2e92d715de91ed3dccd937ae616bba 100644 --- a/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj +++ b/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj @@ -114,4 +114,7 @@ + + + diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs index ae810cd8c3caa65099a9f31b1727094db2d2a7d8..c0d0b830dd8a76a140a1605d44e610265c8819be 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs @@ -33,7 +33,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem using Workspace = Microsoft.CodeAnalysis.Workspace; // NOTE: Microsoft.VisualStudio.LanguageServices.TypeScript.TypeScriptProject derives from AbstractProject. +#pragma warning disable CS0618 // IVisualStudioHostProject is obsolete internal abstract partial class AbstractProject : ForegroundThreadAffinitizedObject, IVisualStudioHostProject +#pragma warning restore CS0618 // IVisualStudioHostProject is obsolete { internal static object RuleSetErrorId = new object(); private readonly object _gate = new object(); @@ -598,7 +600,7 @@ protected int AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(s // at this point, we don't know whether it is a metadata reference added because // we don't have enough information yet for p2p reference or user explicitly added it // as a metadata reference. - AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(this, filePath, properties)); + AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(filePath, properties)); // here, we change behavior compared to old C# language service. regardless of file being exist or not, // we will always return S_OK. this is to support cross language p2p reference better. @@ -1228,7 +1230,7 @@ internal void UndoProjectReferenceConversionForDisappearingOutputPath(string bin projectReference.Aliases, projectReference.EmbedInteropTypes); - AddMetadataReferenceCore(MetadataReferenceProvider.CreateMetadataReference(this, binPath, metadataReferenceProperties)); + AddMetadataReferenceCore(MetadataReferenceProvider.CreateMetadataReference(binPath, metadataReferenceProperties)); Contract.ThrowIfFalse(RemoveMetadataFileNameToConvertedProjectReference(binPath)); } @@ -1255,7 +1257,7 @@ protected void UpdateMetadataReferenceAliases(string file, ImmutableArray internal class DocumentKey : IEquatable { - private readonly IVisualStudioHostProject _hostProject; + private readonly AbstractProject _hostProject; private readonly string _moniker; - public IVisualStudioHostProject HostProject { get { return _hostProject; } } + public AbstractProject HostProject { get { return _hostProject; } } public string Moniker { get { return _moniker; } } - public DocumentKey(IVisualStudioHostProject hostProject, string moniker) + public DocumentKey(AbstractProject hostProject, string moniker) { Contract.ThrowIfNull(hostProject); Contract.ThrowIfNull(moniker); diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.StandardTextDocument.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.StandardTextDocument.cs index 8e3f3c9f90d644f3650ce0412561a29cb1d02120..9b9580a71415424628ab2e7e1dfd369710104a18 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.StandardTextDocument.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.StandardTextDocument.cs @@ -26,7 +26,6 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS /// private readonly DocumentProvider _documentProvider; private readonly string _itemMoniker; - private readonly ITextUndoHistoryRegistry _textUndoHistoryRegistry; private readonly FileChangeTracker _fileChangeTracker; private readonly ReiteratedVersionSnapshotTracker _snapshotTracker; private readonly TextLoader _doNotAccessDirectlyLoader; @@ -38,7 +37,7 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS public DocumentId Id { get; } public IReadOnlyList Folders { get; } - public IVisualStudioHostProject Project { get; } + public AbstractProject Project { get; } public SourceCodeKind SourceCodeKind { get; } public DocumentKey Key { get; } @@ -52,11 +51,10 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS /// public StandardTextDocument( DocumentProvider documentProvider, - IVisualStudioHostProject project, + AbstractProject project, DocumentKey documentKey, Func> getFolderNames, SourceCodeKind sourceCodeKind, - ITextUndoHistoryRegistry textUndoHistoryRegistry, IVsFileChangeEx fileChangeService, ITextBuffer openTextBuffer, DocumentId id, @@ -79,7 +77,6 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS this.Key = documentKey; this.SourceCodeKind = sourceCodeKind; - _textUndoHistoryRegistry = textUndoHistoryRegistry; _fileChangeTracker = new FileChangeTracker(fileChangeService, this.FilePath); _fileChangeTracker.UpdatedOnDisk += OnUpdatedOnDisk; @@ -211,42 +208,20 @@ public void UpdateText(SourceText newText) // expensive source control check if the file is already checked out. if (_openTextBuffer != null) { - UpdateText(newText, _openTextBuffer, EditOptions.DefaultMinimalChange); + TextEditApplication.UpdateText(newText, _openTextBuffer, EditOptions.DefaultMinimalChange); } else { using (var invisibleEditor = ((VisualStudioWorkspaceImpl)this.Project.Workspace).OpenInvisibleEditor(this)) { - UpdateText(newText, invisibleEditor.TextBuffer, EditOptions.None); + TextEditApplication.UpdateText(newText, invisibleEditor.TextBuffer, EditOptions.None); } } } - private static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) + public ITextBuffer GetTextUndoHistoryBuffer() { - using (var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null)) - { - var oldSnapshot = buffer.CurrentSnapshot; - var oldText = oldSnapshot.AsText(); - var changes = newText.GetTextChanges(oldText); - if (CodeAnalysis.Workspace.TryGetWorkspace(oldText.Container, out var workspace)) - { - var undoService = workspace.Services.GetService(); - undoService.BeginUndoTransaction(oldSnapshot); - } - - foreach (var change in changes) - { - edit.Replace(change.Span.Start, change.Span.Length, change.NewText); - } - - edit.ApplyAndLogExceptions(); - } - } - - public ITextUndoHistory GetTextUndoHistory() - { - return _textUndoHistoryRegistry.GetHistory(GetOpenTextBuffer()); + return GetOpenTextBuffer(); } private string GetDebuggerDisplay() diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs index 34bcbe1f9144ff72c36d20935fb2c3dae090a254..c850ded90711d95be96b027504adc10a027c93ae 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/DocumentProvider.cs @@ -30,10 +30,9 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje #region Immutable readonly fields/properties that can be accessed from foreground or background threads - do not need locking for access. private readonly object _gate = new object(); private readonly uint _runningDocumentTableEventCookie; - private readonly IVisualStudioHostProjectContainer _projectContainer; + private readonly VisualStudioProjectTracker _projectTracker; private readonly IVsFileChangeEx _fileChangeService; private readonly IVsTextManager _textManager; - private readonly ITextUndoHistoryRegistry _textUndoHistoryRegistry; private readonly IVsRunningDocumentTable4 _runningDocumentTable; private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService; private readonly IContentTypeRegistryService _contentTypeRegistryService; @@ -54,22 +53,20 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje /// /// Creates a document provider. /// - /// Project container for the documents. /// Service provider /// An optional to track active and visible documents. public DocumentProvider( - IVisualStudioHostProjectContainer projectContainer, + VisualStudioProjectTracker projectTracker, IServiceProvider serviceProvider, VisualStudioDocumentTrackingService documentTrackingService) { var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel)); - _projectContainer = projectContainer; + _projectTracker = projectTracker; this._documentTrackingServiceOpt = documentTrackingService; this._runningDocumentTable = (IVsRunningDocumentTable4)serviceProvider.GetService(typeof(SVsRunningDocumentTable)); this._editorAdaptersFactoryService = componentModel.GetService(); this._contentTypeRegistryService = componentModel.GetService(); - _textUndoHistoryRegistry = componentModel.GetService(); _textManager = (IVsTextManager)serviceProvider.GetService(typeof(SVsTextManager)); _fileChangeService = (IVsFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx)); @@ -85,6 +82,28 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje Marshal.ThrowExceptionForHR(runningDocumentTableForEvents.AdviseRunningDocTableEvents(new RunningDocTableEventsSink(this), out _runningDocumentTableEventCookie)); } + [Obsolete("This overload is a compatibility shim for TypeScript; please do not use it.")] + public IVisualStudioHostDocument TryGetDocumentForFile( + IVisualStudioHostProject hostProject, + string filePath, + SourceCodeKind sourceCodeKind, + Func canUseTextBuffer, + Func> getFolderNames, + EventHandler updatedOnDiskHandler = null, + EventHandler openedHandler = null, + EventHandler closingHandler = null) + { + return TryGetDocumentForFile( + (AbstractProject)hostProject, + filePath, + sourceCodeKind, + canUseTextBuffer, + getFolderNames, + updatedOnDiskHandler, + openedHandler, + closingHandler); + } + /// /// Gets the for the file at the given filePath. /// If we are on the foreground thread and this document is already open in the editor, @@ -93,7 +112,7 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje /// whenever is invoked for the returned document. /// public IVisualStudioHostDocument TryGetDocumentForFile( - IVisualStudioHostProject hostProject, + AbstractProject hostProject, string filePath, SourceCodeKind sourceCodeKind, Func canUseTextBuffer, @@ -163,7 +182,6 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje documentKey, getFolderNames, sourceCodeKind, - _textUndoHistoryRegistry, _fileChangeService, openTextBuffer, id, @@ -334,7 +352,7 @@ private void TryProcessOpenForDocCookie_NoLock(uint docCookie) if (_runningDocumentTable.GetDocumentData(docCookie) is IVsTextBuffer shimTextBuffer) { var hasAssociatedRoslynDocument = false; - foreach (var project in _projectContainer.GetProjects()) + foreach (var project in _projectTracker.ImmutableProjects) { var documentKey = new DocumentKey(project, moniker); @@ -391,11 +409,11 @@ private void TryProcessOpenForDocCookie_NoLock(uint docCookie) { // This is opening some other designer or property page. If it's tied to our IVsHierarchy, we should // let the workspace know - foreach (var project in _projectContainer.GetProjects()) + foreach (var project in _projectTracker.ImmutableProjects) { if (hierarchy == project.Hierarchy) { - _projectContainer.NotifyNonDocumentOpenedForProject(project); + _projectTracker.NotifyNonDocumentOpenedForProject(project); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostDocument.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostDocument.cs index 6c54dc04524ce03b5ecc1f48dc6d0b8f882973a2..7689adb7c402949212a3a333897d1624d53de270 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostDocument.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostDocument.cs @@ -22,7 +22,7 @@ internal interface IVisualStudioHostDocument : IDisposable /// /// The visual studio project this document is part of. /// - IVisualStudioHostProject Project { get; } + AbstractProject Project { get; } /// /// The Visual Studio identity of the document within its project. @@ -113,8 +113,8 @@ internal interface IVisualStudioHostDocument : IDisposable void UpdateText(SourceText newText); /// - /// Fetches the that should be used to undo edits to this document. + /// Fetches the that should be used to undo edits to this document. /// - ITextUndoHistory GetTextUndoHistory(); + ITextBuffer GetTextUndoHistoryBuffer(); } } diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostProject.cs index 34e4f5621f7aaa885e5923616e837797cd0d9b32..5b86e708fd0f496459172bccfdd4f03de7dad014 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostProject.cs @@ -2,48 +2,15 @@ using System; using Microsoft.CodeAnalysis; -using Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { /// - /// The interface implemented by all types of projects within Visual Studio (like regular - /// projects, Miscellaneous files projects, etc.) + /// This interface only exists to maintain an overload of . /// + [Obsolete("This overload is a compatibility shim for TypeScript; please do not use it.")] internal interface IVisualStudioHostProject { ProjectId Id { get; } - string Language { get; } - - /// - /// The for this project. NOTE: May be null in Deferred Project Load cases. - /// - IVsHierarchy Hierarchy { get; } - Guid Guid { get; } - - Microsoft.CodeAnalysis.Workspace Workspace { get; } - - /// - /// The public display name of the project. This name is not unique and may be shared - /// between multiple projects, especially in cases like Venus where the intellisense - /// projects will match the name of their logical parent project. - /// - string DisplayName { get; } - - /// - /// The name of the project according to the project system. In "regular" projects this is - /// equivalent to , but in Venus cases these will differ. The - /// ProjectSystemName is the 2_Default.aspx project name, whereas the regular display name - /// matches the display name of the project the user actually sees in the solution explorer. - /// These can be assumed to be unique within the Visual Studio workspace. - /// - string ProjectSystemName { get; } - - IVisualStudioHostDocument GetDocumentOrAdditionalDocument(DocumentId id); - IVisualStudioHostDocument GetCurrentDocumentFromPath(string filePath); - - ProjectInfo CreateProjectInfoForCurrentState(); - - bool ContainsFile(string moniker); } } diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostProjectContainer.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostProjectContainer.cs deleted file mode 100644 index ed1b129558a8da5f325ef6be35a7069e43ae5098..0000000000000000000000000000000000000000 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/IVisualStudioHostProjectContainer.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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.Collections.Generic; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem -{ - /// - /// An interface implemented by a workspace to get the set of host projects contained in the - /// workspace. - /// - internal interface IVisualStudioHostProjectContainer - { - IReadOnlyList GetProjects(); - - void NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project); - } -} diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/InvisibleEditor.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/InvisibleEditor.cs index 2f4728a5b20fe3b9748251768ee700b6eaf26b9d..deaf7be84b9642c22380c6c78d7bce080b9e42d4 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/InvisibleEditor.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/InvisibleEditor.cs @@ -35,7 +35,7 @@ internal partial class InvisibleEditor : IInvisibleEditor /// , which performs a much slower query of all /// projects in the solution. /// - public InvisibleEditor(IServiceProvider serviceProvider, string filePath, IVisualStudioHostProject projectOpt, bool needsSave, bool needsUndoDisabled) + public InvisibleEditor(IServiceProvider serviceProvider, string filePath, AbstractProject projectOpt, bool needsSave, bool needsUndoDisabled) { _serviceProvider = serviceProvider; _filePath = filePath; diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/LinkedFileUtilities.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/LinkedFileUtilities.cs index 43a024ca2a61966fb10fdb9a71eb43c865bd9c1b..6c72fce22c0f03e31e7b747f8bf516676834d8ec 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/LinkedFileUtilities.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/LinkedFileUtilities.cs @@ -118,23 +118,23 @@ private IVsHierarchy GetSharedHierarchyForItemInternal(IVsHierarchy headProjectH : null; } - public static IVisualStudioHostProject GetContextHostProject(IVsHierarchy sharedHierarchy, IVisualStudioHostProjectContainer hostProjectContainer) + public static AbstractProject GetContextHostProject(IVsHierarchy sharedHierarchy, VisualStudioProjectTracker projectTracker) { - return s_singleton.GetContextHostProjectInternal(sharedHierarchy, hostProjectContainer); + return s_singleton.GetContextHostProjectInternal(sharedHierarchy, projectTracker); } - private IVisualStudioHostProject GetContextHostProjectInternal(IVsHierarchy hierarchy, IVisualStudioHostProjectContainer hostProjectContainer) + private AbstractProject GetContextHostProjectInternal(IVsHierarchy hierarchy, VisualStudioProjectTracker projectTracker) { hierarchy = GetSharedItemContextHierarchy(hierarchy) ?? hierarchy; var projectName = GetActiveIntellisenseProjectContextInternal(hierarchy); if (projectName != null) { - return hostProjectContainer.GetProjects().FirstOrDefault(p => p.ProjectSystemName == projectName); + return projectTracker.ImmutableProjects.FirstOrDefault(p => p.ProjectSystemName == projectName); } else { - return hostProjectContainer.GetProjects().FirstOrDefault(p => p.Hierarchy == hierarchy); + return projectTracker.ImmutableProjects.FirstOrDefault(p => p.Hierarchy == hierarchy); } } diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.Snapshot.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.Snapshot.cs index e23995c08940611fdfaa82f06752a8e4f56bce06..ef977368d234e3aa6c75eb8b884734fb3e76dd8c 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.Snapshot.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.Snapshot.cs @@ -24,7 +24,7 @@ internal partial class VisualStudioMetadataReference /// When the compilation is recreated for a solution the compiler asks for metadata again and we need to provide the original content, /// not read the file again. Therefore we need to save the timestamp on the . /// - /// When the VS observes a change in a metadata reference file the version is advanced and a new instance of + /// When the VS observes a change in a metadata reference file the project version is advanced and a new instance of /// is created for the corresponding reference. /// [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.cs index 77cc71b5445f84a8e1422d1a838e0f21c81615e1..3a72d48300f953303bf5426c7154baa33633b237 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReference.cs @@ -12,7 +12,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem internal sealed partial class VisualStudioMetadataReference : IDisposable { private readonly VisualStudioMetadataReferenceManager _provider; - private readonly IVisualStudioHostProject _hostProject; private readonly MetadataReferenceProperties _properties; private readonly FileChangeTracker _fileChangeTracker; @@ -22,14 +21,12 @@ internal sealed partial class VisualStudioMetadataReference : IDisposable public VisualStudioMetadataReference( VisualStudioMetadataReferenceManager provider, - IVisualStudioHostProject hostProject, string filePath, MetadataReferenceProperties properties) { Contract.ThrowIfTrue(properties.Kind != MetadataImageKind.Assembly); _provider = provider; - _hostProject = hostProject; _properties = properties; // We don't track changes to netmodules linked to the assembly. @@ -44,11 +41,6 @@ public string FilePath get { return _fileChangeTracker.FilePath; } } - public IVisualStudioHostProject Project - { - get { return _hostProject; } - } - public MetadataReferenceProperties Properties { get { return _properties; } diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReferenceManager.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReferenceManager.cs index 4e2e149f4f021e08fdc74e5b3097c01f9bcc7199..367c87705b03345016f57b89a1e32536e4d6e42c 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReferenceManager.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MetadataReferences/VisualStudioMetadataReferenceManager.cs @@ -86,9 +86,9 @@ public PortableExecutableReference CreateMetadataReferenceSnapshot(string filePa return new VisualStudioMetadataReference.Snapshot(this, properties, filePath); } - public VisualStudioMetadataReference CreateMetadataReference(IVisualStudioHostProject hostProject, string filePath, MetadataReferenceProperties properties) + public VisualStudioMetadataReference CreateMetadataReference(string filePath, MetadataReferenceProperties properties) { - return new VisualStudioMetadataReference(this, hostProject, filePath, properties); + return new VisualStudioMetadataReference(this, filePath, properties); } public void ClearCache() diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.HostProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.HostProject.cs deleted file mode 100644 index d34b414d574bc69ade401fb5190a41d62cf7b86d..0000000000000000000000000000000000000000 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.HostProject.cs +++ /dev/null @@ -1,119 +0,0 @@ -// 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.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using EnvDTE; -using Microsoft.CodeAnalysis; -using Roslyn.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem -{ - using Workspace = Microsoft.CodeAnalysis.Workspace; - - internal partial class MiscellaneousFilesWorkspace - { - private sealed class HostProject : IVisualStudioHostProject - { - public ProjectId Id { get; } - public string Language { get; } - - internal IVisualStudioHostDocument Document { get; set; } - - private readonly string _assemblyName; - private readonly ParseOptions _parseOptionsOpt; - private readonly CompilationOptions _compilationOptionsOpt; - private readonly IEnumerable _metadataReferences; - private readonly VersionStamp _version; - private readonly Workspace _workspace; - - public HostProject(Workspace workspace, SolutionId solutionId, string languageName, ParseOptions parseOptionsOpt, CompilationOptions compilationOptionsOpt, IEnumerable metadataReferences) - { - Debug.Assert(workspace != null); - Debug.Assert(languageName != null); - Debug.Assert(metadataReferences != null); - - _workspace = workspace; - _parseOptionsOpt = parseOptionsOpt; - _compilationOptionsOpt = compilationOptionsOpt; - - Id = ProjectId.CreateNewId(debugName: "Miscellaneous Files"); - Language = languageName; - - // the assembly name must be unique for each collection of loose files. since the name doesn't matter - // a random GUID can be used. - _assemblyName = Guid.NewGuid().ToString("N"); - - _version = VersionStamp.Create(); - _metadataReferences = metadataReferences; - } - - public ProjectInfo CreateProjectInfoForCurrentState() - { - var info = ProjectInfo.Create( - Id, - _version, - name: ServicesVSResources.Miscellaneous_Files, - assemblyName: _assemblyName, - language: Language, - filePath: null, - compilationOptions: _compilationOptionsOpt, - parseOptions: _parseOptionsOpt, - documents: SpecializedCollections.EmptyEnumerable(), - projectReferences: SpecializedCollections.EmptyEnumerable(), - metadataReferences: _metadataReferences, - isSubmission: false, - hostObjectType: null); - - // misc project will never be fully loaded since, by defintion, it won't know - // what the full set of information is. - return info.WithHasAllInformation(hasAllInformation: false); - } - - public Microsoft.VisualStudio.Shell.Interop.IVsHierarchy Hierarchy => null; - - public Guid Guid => Guid.Empty; - - public string ProjectType => Constants.vsProjectKindMisc; - - public Workspace Workspace => _workspace; - - public string DisplayName => "MiscellaneousFiles"; - public string ProjectSystemName => DisplayName; - - public IVisualStudioHostDocument GetDocumentOrAdditionalDocument(DocumentId id) - { - if (id == this.Document.Id) - { - return this.Document; - } - else - { - return null; - } - } - - public IVisualStudioHostDocument GetCurrentDocumentFromPath(string filePath) - { - if (Document == null || !filePath.Equals(Document.Key.Moniker, StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - return Document; - } - - public bool ContainsFile(string moniker) - { - // We may have created our project but haven't created the document yet for it - if (Document == null) - { - return false; - } - - return moniker.Equals(Document.Key.Moniker, StringComparison.OrdinalIgnoreCase); - } - } - } -} diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs index 9fcf1f777eab85515a1b6fc0e2a7a1b76a365c4e..beb9f8f9d44e37f99f2ec3ed0a623169beffefc6 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -17,6 +17,7 @@ using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.TextManager.Interop; using Roslyn.Utilities; @@ -25,15 +26,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem using Workspace = Microsoft.CodeAnalysis.Workspace; [Export(typeof(MiscellaneousFilesWorkspace))] - internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunningDocTableEvents2, IVisualStudioHostProjectContainer + internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunningDocTableEvents2 { private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService; private readonly IMetadataAsSourceFileService _fileTrackingMetadataAsSourceService; private readonly IVsRunningDocumentTable4 _runningDocumentTable; private readonly IVsTextManager _textManager; - private readonly DocumentProvider _documentProvider; - private readonly Dictionary _languageInformationByLanguageGuid = new Dictionary(); /// @@ -42,8 +41,11 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin /// private IBidirectionalMap _docCookieToWorkspaceRegistration = BidirectionalMap.Empty; - private readonly Dictionary _hostProjects = new Dictionary(); - private readonly Dictionary _docCookiesToHostProject = new Dictionary(); + /// + /// The mapping of all doc cookies in the RDT and the of the project and of the open + /// file we have created for that open buffer. An entry should only be in here if it's also already in . + /// + private readonly Dictionary _docCookiesToProjectIdAndContainer = new Dictionary(); private readonly ImmutableArray _metadataReferences; private uint _runningDocumentTableEventsCookie; @@ -69,7 +71,6 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin ((IVsRunningDocumentTable)_runningDocumentTable).AdviseRunningDocTableEvents(this, out _runningDocumentTableEventsCookie); _metadataReferences = ImmutableArray.CreateRange(CreateMetadataReferences()); - _documentProvider = new DocumentProvider(this, serviceProvider, documentTrackingService: null); saveEventsService.StartSendingSaveEvents(); } @@ -140,7 +141,7 @@ public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarch { var moniker = _runningDocumentTable.GetDocumentMoniker(docCookie); - if (moniker != null && TryGetLanguageInformation(moniker) != null && !_docCookiesToHostProject.ContainsKey(docCookie)) + if (moniker != null && TryGetLanguageInformation(moniker) != null && !_docCookiesToProjectIdAndContainer.ContainsKey(docCookie)) { TrackOpenedDocument(docCookie, moniker); } @@ -256,15 +257,12 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e) if (workspaceRegistration.Workspace == null) { - if (_docCookiesToHostProject.TryGetValue(docCookie, out var hostProject)) + if (_docCookiesToProjectIdAndContainer.TryGetValue(docCookie, out var projectIdAndSourceTextContainer)) { // The workspace was taken from us and released and we have only asynchronously found out now. - var document = hostProject.Document; - - if (document.IsOpen) - { - RegisterText(document.GetOpenTextContainer()); - } + // We already have the file open in our workspace, but the global mapping of source text container + // to the workspace that owns it needs to be updated once more. + RegisterText(projectIdAndSourceTextContainer.textContainer); } else { @@ -281,7 +279,7 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e) else if (IsClaimedByAnotherWorkspace(workspaceRegistration)) { // It's now claimed by another workspace, so we should unclaim it - if (_docCookiesToHostProject.ContainsKey(docCookie)) + if (_docCookiesToProjectIdAndContainer.ContainsKey(docCookie)) { DetachFromDocument(docCookie, moniker); } @@ -330,17 +328,32 @@ private void AttachToDocument(uint docCookie, string moniker) return; } + var projectInfo = CreateProjectInfoForDocument(moniker); + + OnProjectAdded(projectInfo); + + var sourceTextContainer = textBuffer.AsTextContainer(); + OnDocumentOpened(projectInfo.Documents.Single().Id, sourceTextContainer); + + _docCookiesToProjectIdAndContainer.Add(docCookie, (projectInfo.Id, sourceTextContainer)); + } + + /// + /// Creates the that can be added to the workspace for a newly opened document. + /// + private ProjectInfo CreateProjectInfoForDocument(string filePath) + { // This should always succeed since we only got here if we already confirmed the moniker is acceptable - var languageInformation = TryGetLanguageInformation(moniker); + var languageInformation = TryGetLanguageInformation(filePath); Contract.ThrowIfNull(languageInformation); var languageServices = Services.GetLanguageServices(languageInformation.LanguageName); var compilationOptionsOpt = languageServices.GetService()?.GetDefaultCompilationOptions(); var parseOptionsOpt = languageServices.GetService()?.GetDefaultParseOptions(); - if (parseOptionsOpt != null && + if (parseOptionsOpt != null && compilationOptionsOpt != null && - PathUtilities.GetExtension(moniker) == languageInformation.ScriptExtension) + PathUtilities.GetExtension(filePath) == languageInformation.ScriptExtension) { parseOptionsOpt = parseOptionsOpt.WithKind(SourceCodeKind.Script); @@ -350,7 +363,7 @@ private void AttachToDocument(uint docCookie, string moniker) // Misc files workspace always provides the service: Contract.ThrowIfNull(scriptEnvironmentService); - var baseDirectory = PathUtilities.GetDirectoryName(moniker); + var baseDirectory = PathUtilities.GetDirectoryName(filePath); // TODO (https://github.com/dotnet/roslyn/issues/5325, https://github.com/dotnet/roslyn/issues/13886): // - Need to have a way to specify these somewhere in VS options. @@ -367,40 +380,34 @@ private void AttachToDocument(uint docCookie, string moniker) WithSourceReferenceResolver(new SourceFileResolver(scriptEnvironmentService.SourceReferenceSearchPaths, baseDirectory)); } - // First, create the project - var hostProject = new HostProject(this, CurrentSolution.Id, languageInformation.LanguageName, parseOptionsOpt, compilationOptionsOpt, _metadataReferences); - - // Now try to find the document. We accept any text buffer, since we've already verified it's an appropriate file in ShouldIncludeFile. - var document = _documentProvider.TryGetDocumentForFile( - hostProject, - moniker, - parseOptionsOpt?.Kind ?? SourceCodeKind.Regular, - getFolderNames: _ => SpecializedCollections.EmptyReadOnlyList(), - canUseTextBuffer: _ => true); - - // If the buffer has not yet been initialized, we won't get a document. - if (document == null) - { - return; - } - - // Since we have a document, we can do the rest of the project setup. - _hostProjects.Add(hostProject.Id, hostProject); - OnProjectAdded(hostProject.CreateProjectInfoForCurrentState()); - - OnDocumentAdded(document.GetInitialState()); - hostProject.Document = document; - - // Notify the document provider, so it knows the document is now open and a part of - // the project - _documentProvider.NotifyDocumentRegisteredToProjectAndStartToRaiseEvents(document); - - Contract.ThrowIfFalse(document.IsOpen); - - var buffer = document.GetOpenTextBuffer(); - OnDocumentOpened(document.Id, document.GetOpenTextContainer()); - - _docCookiesToHostProject.Add(docCookie, hostProject); + var projectId = ProjectId.CreateNewId(debugName: "Miscellaneous Files Project for " + filePath); + var documentId = DocumentId.CreateNewId(projectId, debugName: filePath); + + var documentInfo = DocumentInfo.Create( + documentId, + filePath, + sourceCodeKind: parseOptionsOpt?.Kind ?? SourceCodeKind.Regular, + loader: new FileTextLoader(filePath, defaultEncoding: null), + filePath: filePath); + + // The assembly name must be unique for each collection of loose files. Since the name doesn't matter + // a random GUID can be used. + string assemblyName = Guid.NewGuid().ToString("N"); + + var projectInfo = ProjectInfo.Create( + projectId, + VersionStamp.Create(), + name: ServicesVSResources.Miscellaneous_Files, + assemblyName, + languageInformation.LanguageName, + compilationOptions: compilationOptionsOpt, + parseOptions: parseOptionsOpt, + documents: SpecializedCollections.SingletonEnumerable(documentInfo), + metadataReferences: _metadataReferences); + + // Miscellaneous files projects are never fully loaded since, by definition, it won't know + // what the full set of information is. + return projectInfo.WithHasAllInformation(hasAllInformation: false); } private void DetachFromDocument(uint docCookie, string moniker) @@ -411,18 +418,15 @@ private void DetachFromDocument(uint docCookie, string moniker) return; } - if (_docCookiesToHostProject.TryGetValue(docCookie, out var hostProject)) + if (_docCookiesToProjectIdAndContainer.TryGetValue(docCookie, out var projectIdAndContainer)) { - var document = hostProject.Document; - - OnDocumentClosed(document.Id, document.Loader); - OnDocumentRemoved(document.Id); - OnProjectRemoved(hostProject.Id); + var document = this.CurrentSolution.GetProject(projectIdAndContainer.projectId).Documents.Single(); - _hostProjects.Remove(hostProject.Id); - _docCookiesToHostProject.Remove(docCookie); + // We must close the document prior to deleting the project + OnDocumentClosed(document.Id, new FileTextLoader(document.FilePath, defaultEncoding: null)); + OnProjectRemoved(document.Project.Id); - document.Dispose(); + _docCookiesToProjectIdAndContainer.Remove(docCookie); return; } @@ -452,35 +456,14 @@ public override bool CanApplyChange(ApplyChangesKind feature) protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText newText) { - var hostDocument = this.GetDocument(documentId); - hostDocument.UpdateText(newText); - } - - private HostProject GetHostProject(ProjectId id) - { - _hostProjects.TryGetValue(id, out var project); - return project; - } - - internal IVisualStudioHostDocument GetDocument(DocumentId id) - { - var project = GetHostProject(id.ProjectId); - if (project != null && project.Document.Id == id) + foreach (var projectIdAndSourceTextContainer in _docCookiesToProjectIdAndContainer.Values) { - return project.Document; + if (projectIdAndSourceTextContainer.projectId == documentId.ProjectId) + { + TextEditApplication.UpdateText(newText, projectIdAndSourceTextContainer.textContainer.GetTextBuffer(), EditOptions.DefaultMinimalChange); + break; + } } - - return null; - } - - IReadOnlyList IVisualStudioHostProjectContainer.GetProjects() - { - return _hostProjects.Values.ToImmutableReadOnlyListOrEmpty(); - } - - void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project) - { - // Since the MiscellaneousFilesWorkspace doesn't do anything lazily, this is a no-op } private class LanguageInformation diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/TextEditApplication.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/TextEditApplication.cs new file mode 100644 index 0000000000000000000000000000000000000000..9e4e64a3d343a14d5f78d60fabc9434a2ab76ee7 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/TextEditApplication.cs @@ -0,0 +1,32 @@ +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.Undo; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +{ + internal static class TextEditApplication + { + internal static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) + { + using (var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null)) + { + var oldSnapshot = buffer.CurrentSnapshot; + var oldText = oldSnapshot.AsText(); + var changes = newText.GetTextChanges(oldText); + if (CodeAnalysis.Workspace.TryGetWorkspace(oldText.Container, out var workspace)) + { + var undoService = workspace.Services.GetService(); + undoService.BeginUndoTransaction(oldSnapshot); + } + + foreach (var change in changes) + { + edit.Replace(change.Span.Start, change.Span.Length, change.NewText); + } + + edit.ApplyAndLogExceptions(); + } + } + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs index 85e3d1130d65b43dab4c5c92288e873b7dc3c844..0fca1b9997faa41c7102635f030199be50a32de9 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs @@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { - internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffinitizedObject, IVisualStudioHostProjectContainer + internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffinitizedObject { #region Readonly fields private static readonly ConditionalWeakTable s_workingFolderPathMap = new ConditionalWeakTable(); @@ -92,9 +92,7 @@ internal ImmutableArray ImmutableProjects internal HostWorkspaceServices WorkspaceServices { get; } - IReadOnlyList IVisualStudioHostProjectContainer.GetProjects() => this.ImmutableProjects; - - void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project) + internal void NotifyNonDocumentOpenedForProject(AbstractProject project) { AssertIsForeground(); diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs index 471df955dce903adb89bdb183dfffd38ca7a6d02..f08f586d5f20ba8881186c56f8104c4d2fb42dda 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -108,12 +108,12 @@ internal IVisualStudioHostDocument GetHostDocument(DocumentId documentId) return null; } - internal IVisualStudioHostProject GetHostProject(ProjectId projectId) + internal AbstractProject GetHostProject(ProjectId projectId) { return DeferredState?.ProjectTracker.GetProject(projectId); } - private bool TryGetHostProject(ProjectId projectId, out IVisualStudioHostProject project) + private bool TryGetHostProject(ProjectId projectId, out AbstractProject project) { project = GetHostProject(projectId); return project != null; @@ -241,7 +241,7 @@ public override bool CanApplyChange(ApplyChangesKind feature) } } - private bool TryGetProjectData(ProjectId projectId, out IVisualStudioHostProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project) + private bool TryGetProjectData(ProjectId projectId, out AbstractProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project) { hierarchy = null; project = null; @@ -251,7 +251,7 @@ private bool TryGetProjectData(ProjectId projectId, out IVisualStudioHostProject && hierarchy.TryGetProject(out project); } - internal void GetProjectData(ProjectId projectId, out IVisualStudioHostProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project) + internal void GetProjectData(ProjectId projectId, out AbstractProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project) { if (!TryGetProjectData(projectId, out hostProject, out hierarchy, out project)) { @@ -622,7 +622,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa #endif private ProjectItem AddDocumentToProject( - IVisualStudioHostProject hostProject, + AbstractProject hostProject, EnvDTE.Project project, DocumentId documentId, string documentName, @@ -641,7 +641,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa } private ProjectItem AddDocumentToFolder( - IVisualStudioHostProject hostProject, + AbstractProject hostProject, EnvDTE.Project project, DocumentId documentId, IEnumerable folders, @@ -662,7 +662,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa } private ProjectItem AddDocumentToProjectItems( - IVisualStudioHostProject hostProject, + AbstractProject hostProject, ProjectItems projectItems, DocumentId documentId, string folderPath, @@ -865,7 +865,7 @@ protected override void ApplyAdditionalDocumentTextChanged(DocumentId documentId hostDocument.UpdateText(newText); } - private static string GetPreferredExtension(IVisualStudioHostProject hostProject, SourceCodeKind sourceCodeKind) + private static string GetPreferredExtension(AbstractProject hostProject, SourceCodeKind sourceCodeKind) { // No extension was provided. Pick a good one based on the type of host project. switch (hostProject.Language) diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs index 06462360b86ba9b9c909596f15dda13e48ca8b17..8dd9bad0ceeb4943061a65c50fb369809201e057 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs @@ -181,8 +181,6 @@ public bool IsOpen #pragma warning restore 67 - IVisualStudioHostProject IVisualStudioHostDocument.Project { get { return this.Project; } } - public ITextBuffer GetOpenTextBuffer() { return _containedLanguage.SubjectBuffer; @@ -1150,10 +1148,10 @@ private bool CheckCode(ITextSnapshot snapshot, int position, char ch, string tag return CheckCode(snapshot, position - tag2.Length, tag1); } - public ITextUndoHistory GetTextUndoHistory() + public ITextBuffer GetTextUndoHistoryBuffer() { // In Venus scenarios, the undo history is associated with the data buffer - return _componentModel.GetService().GetHistory(_containedLanguage.DataBuffer); + return _containedLanguage.DataBuffer; } public uint GetItemId() diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTextUndoHistoryWorkspaceServiceFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTextUndoHistoryWorkspaceServiceFactory.cs index 62050cb5c4e4f2188871ee507b8b0bf7d170f3ee..344f7940781bb962e5b77a120dc2af5f8d4320e3 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTextUndoHistoryWorkspaceServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTextUndoHistoryWorkspaceServiceFactory.cs @@ -1,11 +1,6 @@ // 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.Generic; using System.Composition; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Host; @@ -25,9 +20,9 @@ internal class VisualStudioTextUndoHistoryWorkspaceServiceFactory : IWorkspaceSe private readonly ITextUndoHistoryWorkspaceService _serviceSingleton; [ImportingConstructor] - public VisualStudioTextUndoHistoryWorkspaceServiceFactory() + public VisualStudioTextUndoHistoryWorkspaceServiceFactory(ITextUndoHistoryRegistry undoHistoryRegistry) { - _serviceSingleton = new TextUndoHistoryWorkspaceService(); + _serviceSingleton = new TextUndoHistoryWorkspaceService(undoHistoryRegistry); } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) @@ -37,44 +32,48 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) private class TextUndoHistoryWorkspaceService : ITextUndoHistoryWorkspaceService { - public bool TryGetTextUndoHistory(Workspace editorWorkspace, ITextBuffer textBuffer, out ITextUndoHistory undoHistory) + private ITextUndoHistoryRegistry _undoHistoryRegistry; + + public TextUndoHistoryWorkspaceService(ITextUndoHistoryRegistry undoHistoryRegistry) { - undoHistory = null; + _undoHistoryRegistry = undoHistoryRegistry; + } - if (!(editorWorkspace is VisualStudioWorkspaceImpl) && - !(editorWorkspace is MiscellaneousFilesWorkspace)) + public bool TryGetTextUndoHistory(Workspace editorWorkspace, ITextBuffer textBuffer, out ITextUndoHistory undoHistory) + { + switch (editorWorkspace) { - return false; - } + case VisualStudioWorkspaceImpl visualStudioWorkspace: - // TODO: Handle undo if context changes - var documentId = editorWorkspace.GetDocumentIdInCurrentContext(textBuffer.AsTextContainer()); - if (documentId == null) - { - return false; - } + // TODO: Handle undo if context changes + var documentId = editorWorkspace.GetDocumentIdInCurrentContext(textBuffer.AsTextContainer()); + if (documentId == null) + { + undoHistory = null; + return false; + } - var document = GetDocument(editorWorkspace, documentId); - if (document == null) - { - return false; - } + // In the Visual Studio case, there might be projection buffers involved for Venus, + // where we associate undo history with the surface buffer and not the subject buffer. + textBuffer = visualStudioWorkspace.GetHostDocument(documentId).GetTextUndoHistoryBuffer(); - undoHistory = document.GetTextUndoHistory(); - return true; - } + break; + + case MiscellaneousFilesWorkspace miscellaneousFilesWorkspace: + + // Nothing to do in this case: textBuffer is correct! + + break; + + default: + + undoHistory = null; + return false; - private IVisualStudioHostDocument GetDocument(Workspace workspace, DocumentId id) - { - switch (workspace) - { - case VisualStudioWorkspaceImpl visualStudioWorkspace: - return visualStudioWorkspace.GetHostDocument(id); - case MiscellaneousFilesWorkspace miscWorkspace: - return miscWorkspace.GetDocument(id); } - return null; + undoHistory = _undoHistoryRegistry.GetHistory(textBuffer); + return true; } } } diff --git a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb index 2f2c8c42eff3d6b76e2fb31b69868d06ba00dd43..a0c2e56289dff11203b29d3976df53a5ab3dd7e7 100644 --- a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb +++ b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb @@ -19,7 +19,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim Partial Friend MustInherit Class VisualBasicProject Inherits AbstractLegacyProject Implements IVbCompilerProject - Implements IVisualStudioHostProject Private ReadOnly _compilerHost As IVbCompilerHost Private ReadOnly _imports As New List(Of GlobalImport)