未验证 提交 6d9992eb 编写于 作者: C Charles Stoner 提交者: GitHub

Merge pull request #23757 from dotnet/merges/master-to-features/compiler-20171213-080026

Merge master to features/compiler
......@@ -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"
......
......@@ -14,7 +14,7 @@
<!-- The release moniker for our packages. Developers should use "dev" and official builds pick the branch
moniker listed below -->
<RoslynNuGetMoniker Condition="'$(RoslynNuGetMoniker)' == ''">dev</RoslynNuGetMoniker>
<RoslynNuGetMoniker Condition="'$(OfficialBuild)' == 'true'">beta2</RoslynNuGetMoniker>
<RoslynNuGetMoniker Condition="'$(OfficialBuild)' == 'true'">beta3</RoslynNuGetMoniker>
<!-- This is the base of the NuGet versioning for prerelease packages -->
<NuGetPreReleaseVersion>$(RoslynFileVersionBase)-$(RoslynNuGetMoniker)</NuGetPreReleaseVersion>
......
......@@ -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",
......
......@@ -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
......
......@@ -305,14 +305,14 @@ internal virtual ImportChain ImportChain
}
/// <summary>
/// Get <see cref="QuickTypeIdentifierAttributeChecker"/> that can be used to quickly
/// check for TypeIdentifier attribute applications in context of this binder.
/// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for certain attribute applications in context of this binder.
/// </summary>
internal virtual QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker
internal virtual QuickAttributeChecker QuickAttributeChecker
{
get
{
return _next.QuickTypeIdentifierAttributeChecker;
return _next.QuickAttributeChecker;
}
}
......
......@@ -28,14 +28,14 @@ internal override ImportChain ImportChain
}
/// <summary>
/// Get <see cref="QuickTypeIdentifierAttributeChecker"/> that can be used to quickly
/// check for TypeIdentifier attribute applications in context of this binder.
/// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for certain attribute applications in context of this binder.
/// </summary>
internal override QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker
internal override QuickAttributeChecker QuickAttributeChecker
{
get
{
return QuickTypeIdentifierAttributeChecker.Predefined;
return QuickAttributeChecker.Predefined;
}
}
......
......@@ -21,7 +21,8 @@ internal sealed class InContainerBinder : Binder
private readonly Func<ConsList<Symbol>, Imports> _computeImports;
private Imports _lazyImports;
private ImportChain _lazyImportChain;
private QuickTypeIdentifierAttributeChecker _lazyQuickTypeIdentifierAttributeChecker;
private QuickAttributeChecker _lazyQuickAttributeChecker;
private readonly SyntaxList<UsingDirectiveSyntax> _usingsSyntax;
/// <summary>
/// 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;
}
}
}
/// <summary>
......@@ -103,26 +118,26 @@ internal override ImportChain ImportChain
}
/// <summary>
/// Get <see cref="QuickTypeIdentifierAttributeChecker"/> that can be used to quickly
/// check for TypeIdentifier attribute applications in context of this binder.
/// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for certain attribute applications in context of this binder.
/// </summary>
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;
}
}
......
// 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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// It works by maintaining a dictionary of all possible simple names that might map to the given
/// attribute.
/// </remarks>
internal sealed class QuickAttributeChecker
{
private readonly Dictionary<string, QuickAttributes> _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<string, QuickAttributes>(StringComparer.Ordinal);
// NOTE: caller must seal
}
private QuickAttributeChecker(QuickAttributeChecker previous)
{
_nameToAttributeMap = new Dictionary<string, QuickAttributes>(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<UsingDirectiveSyntax> 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
}
}
// 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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// It works by maintaining a dictionary of all possible simple names that might map to a TypeIdentifier
/// attribute.
/// </remarks>
internal class QuickTypeIdentifierAttributeChecker
{
private readonly HashSet<string> _candidates;
#if DEBUG
private bool _sealed;
#endif
public static readonly QuickTypeIdentifierAttributeChecker Predefined = new QuickTypeIdentifierAttributeChecker();
private QuickTypeIdentifierAttributeChecker()
{
_candidates = new HashSet<string>();
_candidates.Add(AttributeDescription.TypeIdentifierAttribute.Name);
#if DEBUG
_sealed = true;
#endif
}
private QuickTypeIdentifierAttributeChecker(QuickTypeIdentifierAttributeChecker previous)
{
_candidates = new HashSet<string>(previous._candidates);
}
private void AddCandidate(string candidate)
{
#if DEBUG
Debug.Assert(!_sealed);
#endif
_candidates.Add(candidate);
}
public QuickTypeIdentifierAttributeChecker AddAliasesIfAny(ImmutableDictionary<string, AliasAndUsingDirective> usingAliases)
{
QuickTypeIdentifierAttributeChecker newChecker = null;
foreach (KeyValuePair<string, AliasAndUsingDirective> 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");
}
}
}
......@@ -1556,6 +1556,33 @@ internal CommonAssemblyWellKnownAttributeData GetSourceDecodedWellKnownAttribute
return (CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
}
/// <summary>
/// This only forces binding of attributes that look like they may be forwarded types attributes (syntactically).
/// </summary>
internal HashSet<NamedTypeSymbol> GetForwardedTypes()
{
CustomAttributesBag<CSharpAttributeData> 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<Cci.SecurityAttribute> GetSecurityAttributes(CustomAttributesBag<CSharpAttributeData> attributesBag)
{
Debug.Assert(attributesBag.IsSealed);
......@@ -2556,13 +2583,14 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti
if (_lazyForwardedTypesFromSource == null)
{
IDictionary<string, NamedTypeSymbol> forwardedTypesFromSource;
CommonAssemblyWellKnownAttributeData<NamedTypeSymbol> wellKnownAttributeData = GetSourceDecodedWellKnownAttributeData();
// Get the TypeForwardedTo attributes with minimal binding to avoid cycle problems
HashSet<NamedTypeSymbol> forwardedTypes = GetForwardedTypes();
if (wellKnownAttributeData != null && wellKnownAttributeData.ForwardedTypes != null)
if (forwardedTypes != null)
{
forwardedTypesFromSource = new Dictionary<string, NamedTypeSymbol>(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?");
......
......@@ -780,13 +780,13 @@ private void CheckPresenceOfTypeIdentifierAttribute()
foreach (SyntaxList<AttributeListSyntax> 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.
......
......@@ -255,19 +255,21 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
/// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param>
/// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed.</param>
/// <param name="binderOpt">Binder to use. If null, <see cref="DeclaringCompilation"/> GetBinderFactory will be used.</param>
/// <param name="attributeMatchesOpt">If specified, only load attributes that match this predicate, and any diagnostics produced will be dropped.</param>
/// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns>
internal bool LoadAndValidateAttributes(
OneOrMany<SyntaxList<AttributeListSyntax>> attributesSyntaxLists,
ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag,
AttributeLocation symbolPart = AttributeLocation.None,
bool earlyDecodingOnly = false,
Binder binderOpt = null)
Binder binderOpt = null,
Func<AttributeSyntax, bool> attributeMatchesOpt = null)
{
var diagnostics = DiagnosticBag.GetInstance();
var compilation = this.DeclaringCompilation;
ImmutableArray<Binder> binders;
ImmutableArray<AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, binderOpt, out binders);
ImmutableArray<AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, attributeMatchesOpt, binderOpt, out binders);
Debug.Assert(!attributesToBind.IsDefault);
ImmutableArray<CSharpAttributeData> boundAttributes;
......@@ -344,8 +346,11 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
bool lazyAttributesStoredOnThisThread = false;
if (lazyCustomAttributesBag.SetAttributes(boundAttributes))
{
this.RecordPresenceOfBadAttributes(boundAttributes);
AddDeclarationDiagnostics(diagnostics);
if (attributeMatchesOpt is null)
{
this.RecordPresenceOfBadAttributes(boundAttributes);
AddDeclarationDiagnostics(diagnostics);
}
lazyAttributesStoredOnThisThread = true;
if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty;
}
......@@ -382,6 +387,7 @@ private void RecordPresenceOfBadAttributes(ImmutableArray<CSharpAttributeData> b
AttributeLocation symbolPart,
DiagnosticBag diagnostics,
CSharpCompilation compilation,
Func<AttributeSyntax, bool> attributeMatchesOpt,
Binder rootBinderOpt,
out ImmutableArray<Binder> binders)
{
......@@ -409,8 +415,22 @@ private void RecordPresenceOfBadAttributes(ImmutableArray<CSharpAttributeData> 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++;
}
}
}
}
}
......
......@@ -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()
......
......@@ -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()
......
......@@ -8374,7 +8374,6 @@ End Class
Assert.False(args.CompilationOptions.PublicSign)
End Sub
<WorkItem(8360, "https://github.com/dotnet/roslyn/issues/8360")>
<Fact>
Public Sub PublicSign_KeyFileRelativePath()
......@@ -8383,6 +8382,20 @@ End Class
parsedArgs.Errors.Verify()
End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath()
Dim parsedArgs = FullParse("/publicsign /keyfile: a.cs", _baseDirectory)
parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("keyfile", ":<file>").WithLocation(1, 1))
End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath2()
Dim parsedArgs = FullParse("/publicsign /keyfile:"""" a.cs", _baseDirectory)
parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("keyfile", ":<file>").WithLocation(1, 1))
End Sub
<ConditionalFact(GetType(WindowsOnly))>
Public Sub CommandLineMisc()
Dim args As VisualBasicCommandLineArguments
......
......@@ -34,6 +34,29 @@ BC37257: Option 'CryptoKeyFile' must be an absolute path.
</errors>)
End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath()
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).
WithPublicSign(True).WithCryptoKeyFile("")
AssertTheseDiagnostics(VisualBasicCompilation.Create("test", options:=options),
<errors>
BC37254: Public sign was specified and requires a public key, but no public key was specified
</errors>)
End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath2()
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).
WithPublicSign(True).WithCryptoKeyFile("""""")
AssertTheseDiagnostics(VisualBasicCompilation.Create("test", options:=options),
<errors>
BC37254: Public sign was specified and requires a public key, but no public key was specified
BC37257: Option 'CryptoKeyFile' must be an absolute path.
</errors>)
End Sub
<Fact>
Public Sub LocalizableErrorArgumentToStringDoesntStackOverflow()
' Error ID is arbitrary
......
......@@ -114,4 +114,7 @@
<InternalsVisibleToTest Include="Microsoft.VisualStudio.IntegrationTest.Utilities" />
<InternalsVisibleToTest Include="Roslyn.VisualStudio.Test.Utilities2" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>
......@@ -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<string
RemoveMetadataReferenceCore(existingReference, disposeReference: true);
AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(this, file, newProperties));
AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(file, newProperties));
}
}
......
......@@ -16,13 +16,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
/// </summary>
internal class DocumentKey : IEquatable<DocumentKey>
{
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);
......
......@@ -26,7 +26,6 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS
/// </summary>
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<string> 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
/// </summary>
public StandardTextDocument(
DocumentProvider documentProvider,
IVisualStudioHostProject project,
AbstractProject project,
DocumentKey documentKey,
Func<uint, IReadOnlyList<string>> 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<ISourceTextUndoService>();
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()
......
......@@ -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
/// <summary>
/// Creates a document provider.
/// </summary>
/// <param name="projectContainer">Project container for the documents.</param>
/// <param name="serviceProvider">Service provider</param>
/// <param name="documentTrackingService">An optional <see cref="VisualStudioDocumentTrackingService"/> to track active and visible documents.</param>
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<IVsEditorAdaptersFactoryService>();
this._contentTypeRegistryService = componentModel.GetService<IContentTypeRegistryService>();
_textUndoHistoryRegistry = componentModel.GetService<ITextUndoHistoryRegistry>();
_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<ITextBuffer, bool> canUseTextBuffer,
Func<uint, IReadOnlyList<string>> getFolderNames,
EventHandler updatedOnDiskHandler = null,
EventHandler<bool> openedHandler = null,
EventHandler<bool> closingHandler = null)
{
return TryGetDocumentForFile(
(AbstractProject)hostProject,
filePath,
sourceCodeKind,
canUseTextBuffer,
getFolderNames,
updatedOnDiskHandler,
openedHandler,
closingHandler);
}
/// <summary>
/// Gets the <see cref="IVisualStudioHostDocument"/> 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 <see cref="NotifyDocumentRegisteredToProjectAndStartToRaiseEvents"/> is invoked for the returned document.
/// </summary>
public IVisualStudioHostDocument TryGetDocumentForFile(
IVisualStudioHostProject hostProject,
AbstractProject hostProject,
string filePath,
SourceCodeKind sourceCodeKind,
Func<ITextBuffer, bool> 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);
}
}
}
......
......@@ -22,7 +22,7 @@ internal interface IVisualStudioHostDocument : IDisposable
/// <summary>
/// The visual studio project this document is part of.
/// </summary>
IVisualStudioHostProject Project { get; }
AbstractProject Project { get; }
/// <summary>
/// The Visual Studio identity of the document within its project.
......@@ -113,8 +113,8 @@ internal interface IVisualStudioHostDocument : IDisposable
void UpdateText(SourceText newText);
/// <summary>
/// Fetches the <see cref="ITextUndoHistory"/> that should be used to undo edits to this document.
/// Fetches the <see cref="ITextBuffer"/> that should be used to undo edits to this document.
/// </summary>
ITextUndoHistory GetTextUndoHistory();
ITextBuffer GetTextUndoHistoryBuffer();
}
}
......@@ -2,48 +2,15 @@
using System;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
/// <summary>
/// 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 <see cref="DocumentProvider.TryGetDocumentForFile(IVisualStudioHostProject, string, CodeAnalysis.SourceCodeKind, System.Func{Text.ITextBuffer, bool}, System.Func{uint, System.Collections.Generic.IReadOnlyList{string}}, System.EventHandler, System.EventHandler{bool}, System.EventHandler{bool})"/>.
/// </summary>
[Obsolete("This overload is a compatibility shim for TypeScript; please do not use it.")]
internal interface IVisualStudioHostProject
{
ProjectId Id { get; }
string Language { get; }
/// <summary>
/// The <see cref="IVsHierarchy"/> for this project. NOTE: May be null in Deferred Project Load cases.
/// </summary>
IVsHierarchy Hierarchy { get; }
Guid Guid { get; }
Microsoft.CodeAnalysis.Workspace Workspace { get; }
/// <summary>
/// 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.
/// </summary>
string DisplayName { get; }
/// <summary>
/// The name of the project according to the project system. In "regular" projects this is
/// equivalent to <see cref="DisplayName"/>, 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.
/// </summary>
string ProjectSystemName { get; }
IVisualStudioHostDocument GetDocumentOrAdditionalDocument(DocumentId id);
IVisualStudioHostDocument GetCurrentDocumentFromPath(string filePath);
ProjectInfo CreateProjectInfoForCurrentState();
bool ContainsFile(string moniker);
}
}
// 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
{
/// <summary>
/// An interface implemented by a workspace to get the set of host projects contained in the
/// workspace.
/// </summary>
internal interface IVisualStudioHostProjectContainer
{
IReadOnlyList<IVisualStudioHostProject> GetProjects();
void NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project);
}
}
......@@ -35,7 +35,7 @@ internal partial class InvisibleEditor : IInvisibleEditor
/// <see cref="IVsUIShellOpenDocument4.IsDocumentInAProject2"/>, which performs a much slower query of all
/// projects in the solution.</para>
/// </remarks>
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;
......
......@@ -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);
}
}
......
......@@ -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 <see cref="Snapshot"/>.
///
/// When the VS observes a change in a metadata reference file the <see cref="Project"/> 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
/// <see cref="Snapshot"/> is created for the corresponding reference.
/// </remarks>
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
......
......@@ -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; }
......
......@@ -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()
......
// 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<MetadataReference> _metadataReferences;
private readonly VersionStamp _version;
private readonly Workspace _workspace;
public HostProject(Workspace workspace, SolutionId solutionId, string languageName, ParseOptions parseOptionsOpt, CompilationOptions compilationOptionsOpt, IEnumerable<MetadataReference> 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<DocumentInfo>(),
projectReferences: SpecializedCollections.EmptyEnumerable<ProjectReference>(),
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);
}
}
}
}
......@@ -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<Guid, LanguageInformation> _languageInformationByLanguageGuid = new Dictionary<Guid, LanguageInformation>();
/// <summary>
......@@ -42,8 +41,11 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin
/// </summary>
private IBidirectionalMap<uint, WorkspaceRegistration> _docCookieToWorkspaceRegistration = BidirectionalMap<uint, WorkspaceRegistration>.Empty;
private readonly Dictionary<ProjectId, HostProject> _hostProjects = new Dictionary<ProjectId, HostProject>();
private readonly Dictionary<uint, HostProject> _docCookiesToHostProject = new Dictionary<uint, HostProject>();
/// <summary>
/// The mapping of all doc cookies in the RDT and the <see cref="ProjectId"/> of the project and <see cref="SourceTextContainer"/> of the open
/// file we have created for that open buffer. An entry should only be in here if it's also already in <see cref="_docCookieToWorkspaceRegistration"/>.
/// </summary>
private readonly Dictionary<uint, (ProjectId projectId, SourceTextContainer textContainer)> _docCookiesToProjectIdAndContainer = new Dictionary<uint, (ProjectId, SourceTextContainer)>();
private readonly ImmutableArray<MetadataReference> _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));
}
/// <summary>
/// Creates the <see cref="ProjectInfo"/> that can be added to the workspace for a newly opened document.
/// </summary>
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<ICompilationFactoryService>()?.GetDefaultCompilationOptions();
var parseOptionsOpt = languageServices.GetService<ISyntaxTreeFactoryService>()?.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<string>(),
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<IVisualStudioHostProject> IVisualStudioHostProjectContainer.GetProjects()
{
return _hostProjects.Values.ToImmutableReadOnlyListOrEmpty<IVisualStudioHostProject>();
}
void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project)
{
// Since the MiscellaneousFilesWorkspace doesn't do anything lazily, this is a no-op
}
private class LanguageInformation
......
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<ISourceTextUndoService>();
undoService.BeginUndoTransaction(oldSnapshot);
}
foreach (var change in changes)
{
edit.Replace(change.Span.Start, change.Span.Length, change.NewText);
}
edit.ApplyAndLogExceptions();
}
}
}
}
......@@ -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<SolutionId, string> s_workingFolderPathMap = new ConditionalWeakTable<SolutionId, string>();
......@@ -92,9 +92,7 @@ internal ImmutableArray<AbstractProject> ImmutableProjects
internal HostWorkspaceServices WorkspaceServices { get; }
IReadOnlyList<IVisualStudioHostProject> IVisualStudioHostProjectContainer.GetProjects() => this.ImmutableProjects;
void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project)
internal void NotifyNonDocumentOpenedForProject(AbstractProject project)
{
AssertIsForeground();
......
......@@ -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<string> 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)
......
......@@ -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<ITextUndoHistoryRegistry>().GetHistory(_containedLanguage.DataBuffer);
return _containedLanguage.DataBuffer;
}
public uint GetItemId()
......
// 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;
}
}
}
......
......@@ -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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册