提交 04273d63 编写于 作者: P Paul Vick

Merge remote-tracking branch 'upstream/master' into merge-master-into-future20160517-150020

......@@ -26,7 +26,7 @@
</ItemDefinitionGroup>
<PropertyGroup Condition="'$(PublicKey)' != '' and '$(SignAssembly)' == 'True'">
<InternalsVisibleToSuffix>, PublicKey=$(PublicKey)</InternalsVisibleToSuffix>
<InternalsVisibleToSuffix>, PublicKey=$(RoslynPublicKey)</InternalsVisibleToSuffix>
<InternalsVisibleToTestSuffix>, PublicKey=$(RoslynInternalKey)</InternalsVisibleToTestSuffix>
<InternalsVisibleToMoqSuffix>, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7</InternalsVisibleToMoqSuffix>
</PropertyGroup>
......
......@@ -12,6 +12,7 @@
<CodeAnalysisRuleSet Condition="'$(CodeAnalysisRuleSet)' == '' AND '$(AnalyzerProject)' == 'true'">$(VSLToolsPath)\Rulesets\AnalyzerProjectRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSet Condition="'$(CodeAnalysisRuleSet)' == ''">$(VSLToolsPath)\Rulesets\Roslyn.ruleset</CodeAnalysisRuleSet>
<RoslynPublicKey>0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9</RoslynPublicKey>
<RoslynInternalKey>002400000480000094000000060200000024000052534131000400000100010055e0217eb635f69281051f9a823e0c7edd90f28063eb6c7a742a19b4f6139778ee0af438f47aed3b6e9f99838aa8dba689c7a71ddb860c96d923830b57bbd5cd6119406ddb9b002cf1c723bf272d6acbb7129e9d6dd5a5309c94e0ff4b2c884d45a55f475cd7dba59198086f61f5a8c8b5e601c0edbf269733f6f578fc8579c2</RoslynInternalKey>
</PropertyGroup>
......@@ -51,7 +52,7 @@
<PropertyGroup>
<AssemblyOriginatorKeyFile>$(VSLToolsPath)\Strong Name Keys\35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
<PublicKey>0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9</PublicKey>
<PublicKey>$(RoslynPublicKey)</PublicKey>
<PublicKeyToken>31BF3856AD364E35</PublicKeyToken>
</PropertyGroup>
</When>
......
Introduction
============
The C# and Visual Basic compilers support a /reportanalyzer switch on
the command line to report additional analyzer information, such as execution time.
The output contains the total wall clock time spent in executing the analyzers and
the relative execution times per-analyzer.
Note that the elapsed time may be less than analyzer execution time because
analyzers can run concurrently. One should use this data only for comparitive analysis to
identify any outlier analyzer which takes signinficantly more time than other analyzers.
Output Format
=============
```
Total analyzer execution time: XYZ seconds.
NOTE: Elapsed time may be less than analyzer execution time because analyzers can run concurrently.
```
Time (s) | % | Analyzer
----------|---|----------------------
xyz1 | X | Analyzer Assembly Identity
xyz2 | Y | - DiagnosticAnalyzer1
xyz3 | Z | - DiagnosticAnalyzer2
Example
=============
Following is an example output from a project built with analyzers:
-------------------------------------------------------------------------------------------------------
```
Total analyzer execution time: 0.503 seconds.
NOTE: Elapsed time may be less than analyzer execution time because analyzers can run concurrently.
Time (s) % Analyzer
0.242 48 Roslyn.Diagnostics.CSharp.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.240 47 Roslyn.Diagnostics.Analyzers.CSharpCodeActionCreateAnalyzer
0.002 <1 Roslyn.Diagnostics.Analyzers.CSharp.CSharpSpecializedEnumerableCreationAnalyzer
<0.001 <1 Roslyn.Diagnostics.Analyzers.CSharp.CSharpDiagnosticDescriptorAccessAnalyzer
<0.001 <1 Roslyn.Diagnostics.Analyzers.CSharpSymbolDeclaredEventAnalyzer
0.137 27 System.Runtime.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.045 8 System.Runtime.Analyzers.AttributeStringLiteralsShouldParseCorrectlyAnalyzer
0.024 4 System.Runtime.Analyzers.SpecifyStringComparisonAnalyzer
0.022 4 System.Runtime.Analyzers.SpecifyIFormatProviderAnalyzer
0.020 3 System.Runtime.Analyzers.ProvideCorrectArgumentsToFormattingMethodsAnalyzer
0.009 1 System.Runtime.Analyzers.CallGCSuppressFinalizeCorrectlyAnalyzer
0.007 1 System.Runtime.Analyzers.DisposableTypesShouldDeclareFinalizerAnalyzer
0.003 <1 System.Runtime.Analyzers.NormalizeStringsToUppercaseAnalyzer
0.003 <1 System.Runtime.Analyzers.InstantiateArgumentExceptionsCorrectlyAnalyzer
0.002 <1 System.Runtime.Analyzers.TestForNaNCorrectlyAnalyzer
0.001 <1 System.Runtime.Analyzers.DoNotUseEnumerableMethodsOnIndexableCollectionsInsteadUseTheCollectionDirectlyAnalyzer
<0.001 <1 System.Runtime.Analyzers.SpecifyCultureInfoAnalyzer
<0.001 <1 System.Runtime.Analyzers.DoNotLockOnObjectsWithWeakIdentityAnalyzer
<0.001 <1 System.Runtime.Analyzers.TestForEmptyStringsUsingStringLengthAnalyzer
<0.001 <1 System.Runtime.Analyzers.AvoidUnsealedAttributesAnalyzer
0.085 16 Roslyn.Diagnostics.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.085 16 Roslyn.Diagnostics.Analyzers.DeclarePublicAPIAnalyzer
0.023 4 System.Runtime.CSharp.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.014 2 System.Runtime.Analyzers.CSharpAvoidZeroLengthArrayAllocationsAnalyzer
0.008 1 System.Runtime.Analyzers.CSharpInitializeStaticFieldsInlineAnalyzer
0.001 <1 System.Runtime.Analyzers.CSharpDoNotRaiseReservedExceptionTypesAnalyzer
<0.001 <1 System.Runtime.Analyzers.CSharpUseOrdinalStringComparisonAnalyzer
<0.001 <1 System.Runtime.Analyzers.CSharpDoNotUseTimersThatPreventPowerStateChangesAnalyzer
<0.001 <1 System.Runtime.Analyzers.CSharpDisposeMethodsShouldCallBaseClassDisposeAnalyzer
0.006 1 System.Runtime.InteropServices.CSharp.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.006 1 System.Runtime.InteropServices.Analyzers.CSharpAlwaysConsumeTheValueReturnedByMethodsMarkedWithPreserveSigAttributeAnalyzer
<0.001 <1 System.Runtime.InteropServices.Analyzers.CSharpMarkBooleanPInvokeArgumentsWithMarshalAsAnalyzer
<0.001 <1 System.Runtime.InteropServices.Analyzers.CSharpUseManagedEquivalentsOfWin32ApiAnalyzer
0.004 <1 System.Runtime.InteropServices.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.004 <1 System.Runtime.InteropServices.Analyzers.PInvokeDiagnosticAnalyzer
0.004 <1 System.Collections.Immutable.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.004 <1 System.Collections.Immutable.Analyzers.DoNotCallToImmutableArrayOnAnImmutableArrayValueAnalyzer
0.001 <1 System.Threading.Tasks.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.001 <1 System.Threading.Tasks.Analyzers.DoNotCreateTasksWithoutPassingATaskSchedulerAnalyzer
0.001 <1 XmlDocumentationComments.CSharp.Analyzers, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
0.001 <1 XmlDocumentationComments.Analyzers.CSharpAvoidUsingCrefTagsWithAPrefixAnalyzer
```
\ No newline at end of file
......@@ -21,16 +21,11 @@ The SARIF standard allows the `properties` property of `result` objects
to contain arbitrary (string, string) key-value pairs.
The keys and values used by the C# and VB compilers are serialized from
the corresponding `Microsoft.CodeAnalysis.Diagnostic` and its
`Microsoft.CodeAnalysis.DiagnosticDescriptor` as follows:
the corresponding `Microsoft.CodeAnalysis.Diagnostic` as follows:
Key | Value
------------------------ | ------------
"severity" | `Diagnostic.Severity` ("Hidden", "Info", "Warning, or "Error")
"warningLevel" | `Diagnostic.WarningLevel` ("1", "2", "3", or "4" for "Warning" severity; omitted otherwise)
"defaultSeverity" | `Diagnostic.DefaultSeverity` ("Hidden", "Info", "Warning, "Error")
"title" | `DiagnosticDesciptor.Title` (omitted if null or empty)
"warningLevel" | `Diagnostic.WarningLevel`
"category" | `Diagnostic.Category`
"helpLink" | `DiagnosticDescriptor.HelpLink` (omitted if null or empty)
"isEnabledByDefault" | `Diagnostic.IsEnabledByDefault` ("True" or "False")
"customProperties.[key]" | `Diagnostic.Properties[key]` (for each key in the dictionary)
"isEnabledByDefault" | `Diagnostic.IsEnabledByDefault
"customProperties" | `Diagnostic.Properties`
......@@ -90,60 +90,62 @@ public class C
""results"": [
{{
""ruleId"": ""CS0169"",
""kind"": ""warning"",
""level"": ""warning"",
""message"": ""The field 'C.x' is never used"",
""locations"": [
{{
""analysisTarget"": [
{{
""uri"": ""{0}"",
""region"": {{
""startLine"": 4,
""startColumn"": 17,
""endLine"": 4,
""endColumn"": 18
}}
""resultFile"": {{
""uri"": ""{0}"",
""region"": {{
""startLine"": 4,
""startColumn"": 17,
""endLine"": 4,
""endColumn"": 18
}}
]
}}
}}
],
""fullMessage"": ""The field 'C.x' is never used"",
""isSuppressedInSource"": false,
""tags"": [
""Compiler"",
""Telemetry""
],
""properties"": {{
""severity"": ""Warning"",
""warningLevel"": ""3"",
""defaultSeverity"": ""Warning"",
""title"": ""Field is never used"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""warningLevel"": 3
}}
}},
{{
""ruleId"": ""CS5001"",
""kind"": ""error"",
""locations"": [
],
""fullMessage"": ""Program does not contain a static 'Main' method suitable for an entry point"",
""isSuppressedInSource"": false,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
],
""level"": ""error"",
""message"": ""Program does not contain a static 'Main' method suitable for an entry point""
}}
],
""rules"": {{
""CS0169"": {{
""id"": ""CS0169"",
""shortDescription"": ""Field is never used"",
""defaultLevel"": ""warning"",
""properties"": {{
""severity"": ""Error"",
""defaultSeverity"": ""Error"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry""
]
}}
}},
""CS5001"": {{
""id"": ""CS5001"",
""defaultLevel"": ""error"",
""properties"": {{
""category"": ""Compiler"",
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
]
}}
}}
]
}}
}}
]
}}", AnalyzerForErrorLogTest.GetEscapedUriForPath(sourceFile));
}}", AnalyzerForErrorLogTest.GetUriForPath(sourceFile));
var expectedText = expectedHeader + expectedIssues;
Assert.Equal(expectedText, actualOutput);
......@@ -185,60 +187,65 @@ public class C
""results"": [
{{
""ruleId"": ""CS0169"",
""kind"": ""warning"",
""level"": ""warning"",
""message"": ""The field 'C.x' is never used"",
""suppressionStates"": [
""suppressedInSource""
],
""locations"": [
{{
""analysisTarget"": [
{{
""uri"": ""{0}"",
""region"": {{
""startLine"": 5,
""startColumn"": 17,
""endLine"": 5,
""endColumn"": 18
}}
""resultFile"": {{
""uri"": ""{0}"",
""region"": {{
""startLine"": 5,
""startColumn"": 17,
""endLine"": 5,
""endColumn"": 18
}}
]
}}
}}
],
""fullMessage"": ""The field 'C.x' is never used"",
""isSuppressedInSource"": true,
""tags"": [
""Compiler"",
""Telemetry""
],
""properties"": {{
""severity"": ""Warning"",
""warningLevel"": ""3"",
""defaultSeverity"": ""Warning"",
""title"": ""Field is never used"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""warningLevel"": 3
}}
}},
{{
""ruleId"": ""CS5001"",
""kind"": ""error"",
""locations"": [
],
""fullMessage"": ""Program does not contain a static 'Main' method suitable for an entry point"",
""isSuppressedInSource"": false,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
],
""level"": ""error"",
""message"": ""Program does not contain a static 'Main' method suitable for an entry point""
}}
],
""rules"": {{
""CS0169"": {{
""id"": ""CS0169"",
""shortDescription"": ""Field is never used"",
""defaultLevel"": ""warning"",
""properties"": {{
""severity"": ""Error"",
""defaultSeverity"": ""Error"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry""
]
}}
}},
""CS5001"": {{
""id"": ""CS5001"",
""defaultLevel"": ""error"",
""properties"": {{
""category"": ""Compiler"",
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
]
}}
}}
]
}}
}}
]
}}", AnalyzerForErrorLogTest.GetEscapedUriForPath(sourceFile));
}}", AnalyzerForErrorLogTest.GetUriForPath(sourceFile));
var expectedText = expectedHeader + expectedIssues;
Assert.Equal(expectedText, actualOutput);
......
......@@ -28,6 +28,7 @@
<Compile Include="Collections\BoxesTest.cs" />
<Compile Include="Collections\ByteSequenceComparerTests.cs" />
<Compile Include="CryptoBlobParserTests.cs" />
<Compile Include="Diagnostics\ErrorLoggerTests.cs" />
<Compile Include="Diagnostics\AnalysisContextInfoTests.cs" />
<Compile Include="Diagnostics\BoxingOperationAnalyzer.cs" />
<Compile Include="Diagnostics\CouldHaveMoreSpecificTypeAnalyzer.cs" />
......@@ -40,6 +41,7 @@
<Compile Include="Emit\CustomDebugInfoTests.cs" />
<Compile Include="FileSystem\PathUtilitiesTests.cs" />
<Compile Include="InternalUtilities\StreamExtensionsTests.cs" />
<Compile Include="InternalUtilities\JsonWriterTests.cs" />
<Compile Include="InternalUtilities\StringExtensionsTests.cs" />
<Compile Include="MetadataReferences\AssemblyIdentityExtensions.cs" />
<Compile Include="MetadataReferences\AssemblyIdentityMapTests.cs" />
......
// 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 Xunit;
using System.IO;
using System;
using System.Globalization;
using System.Text;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics
{
// See also VB and C# command line unit tests for additional coverage.
public class ErrorLoggerTests
{
[Fact]
public void AdditionalLocationsAsRelatedLocations()
{
var stream = new MemoryStream();
using (var logger = new ErrorLogger(stream, "toolName", "1.2.3.4", new Version(1, 2, 3, 4), CultureInfo.InvariantCulture))
{
var mainLocation = Location.Create(@"Z:\MainLocation.cs", new TextSpan(0, 0), new LinePositionSpan(LinePosition.Zero, LinePosition.Zero));
var additionalLocation = Location.Create(@"Z:\AdditionalLocation.cs", new TextSpan(0, 0), new LinePositionSpan(LinePosition.Zero, LinePosition.Zero));
var descriptor = new DiagnosticDescriptor("TST", "_TST_", "", "", DiagnosticSeverity.Error, false);
IEnumerable<Location> additionalLocations = new[] { additionalLocation };
logger.LogDiagnostic(Diagnostic.Create(descriptor, mainLocation, additionalLocations));
}
string expected =
@"{
""$schema"": ""http://json.schemastore.org/sarif-1.0.0-beta.5"",
""version"": ""1.0.0-beta.5"",
""runs"": [
{
""tool"": {
""name"": ""toolName"",
""version"": ""1.2.3.4"",
""fileVersion"": ""1.2.3.4"",
""semanticVersion"": ""1.2.3""
},
""results"": [
{
""ruleId"": ""TST"",
""level"": ""error"",
""locations"": [
{
""resultFile"": {
""uri"": ""file:///Z:/MainLocation.cs"",
""region"": {
""startLine"": 1,
""startColumn"": 1,
""endLine"": 1,
""endColumn"": 1
}
}
}
],
""relatedLocations"": [
{
""physicalLocation"": {
""uri"": ""file:///Z:/AdditionalLocation.cs"",
""region"": {
""startLine"": 1,
""startColumn"": 1,
""endLine"": 1,
""endColumn"": 1
}
}
}
]
}
],
""rules"": {
""TST"": {
""id"": ""TST"",
""shortDescription"": ""_TST_"",
""defaultLevel"": ""error"",
""properties"": {
""isEnabledByDefault"": false
}
}
}
}
]
}";
string actual = Encoding.UTF8.GetString(stream.ToArray());
Assert.Equal(expected, actual);
}
[Fact]
public void DescriptorIdCollision()
{
var descriptors = new[] {
new DiagnosticDescriptor("TST001.001", "_TST001.001_", "", "", DiagnosticSeverity.Warning, true),
new DiagnosticDescriptor("TST001", "_TST001_", "", "", DiagnosticSeverity.Warning, true),
new DiagnosticDescriptor("TST001", "_TST001.002_", "", "", DiagnosticSeverity.Warning, true),
new DiagnosticDescriptor("TST001", "_TST001.003_", "", "", DiagnosticSeverity.Warning, true),
};
var stream = new MemoryStream();
using (var logger = new ErrorLogger(stream, "toolName", "1.2.3.4", new Version(1, 2, 3, 4), CultureInfo.InvariantCulture))
{
for (int i = 0; i < 2; i++)
{
foreach (var descriptor in descriptors)
{
logger.LogDiagnostic(Diagnostic.Create(descriptor, Location.None));
}
}
}
string expected =
@"{
""$schema"": ""http://json.schemastore.org/sarif-1.0.0-beta.5"",
""version"": ""1.0.0-beta.5"",
""runs"": [
{
""tool"": {
""name"": ""toolName"",
""version"": ""1.2.3.4"",
""fileVersion"": ""1.2.3.4"",
""semanticVersion"": ""1.2.3""
},
""results"": [
{
""ruleId"": ""TST001.001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.002"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.003"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001.001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.002"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
},
{
""ruleId"": ""TST001"",
""ruleKey"": ""TST001.003"",
""level"": ""warning"",
""properties"": {
""warningLevel"": 1
}
}
],
""rules"": {
""TST001"": {
""id"": ""TST001"",
""shortDescription"": ""_TST001_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
},
""TST001.001"": {
""id"": ""TST001.001"",
""shortDescription"": ""_TST001.001_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
},
""TST001.002"": {
""id"": ""TST001"",
""shortDescription"": ""_TST001.002_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
},
""TST001.003"": {
""id"": ""TST001"",
""shortDescription"": ""_TST001.003_"",
""defaultLevel"": ""warning"",
""properties"": {
""isEnabledByDefault"": true
}
}
}
}
]
}";
string actual = Encoding.UTF8.GetString(stream.ToArray());
Assert.Equal(expected, actual);
}
}
}
// 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.IO;
using Xunit;
namespace Roslyn.Utilities.UnitTests.InternalUtilities
{
public class JsonWriterTests
{
[Theory]
[InlineData("true", true)]
[InlineData("false", false)]
public void Boolean(string expected, bool value)
{
Assert.StrictEqual(expected, WriteToString(value));
}
[Theory]
[InlineData("-2147483648", int.MinValue)]
[InlineData("-42", -42)]
[InlineData("-1", -1)]
[InlineData("0", 0)]
[InlineData("1", 1)]
[InlineData("42", 42)]
[InlineData("2147483647", int.MaxValue)]
public void Integer(string expected, int value)
{
Assert.StrictEqual(expected, WriteToString(value));
}
[Theory]
// escaped
[InlineData(@"\\", '\\')]
[InlineData(@"\""", '"')]
// unsecaped
[InlineData(@"'", '\'')]
[InlineData(@"/", '/')]
//
// There are 5 ranges of characters that have output in the form of "\u<code>".
// The following tests the start and end character and at least one character in each range
// and also in between the ranges.
//
// #1. 0x0000 - 0x001F
[InlineData(@"\u0000", (char)0x00)]
[InlineData(@"\u0010", (char)0x10)]
[InlineData(@"\u001f", (char)0x1f)]
// Between #1 and #2
[InlineData(@"a", (char)0x61)]
// #2. 0x0085 NEXT LINE
[InlineData(@"\u0085", (char)0x85)]
// Between #2 and #3
[InlineData(@"ñ", (char)0xF1)]
// #3. 0x2028 LINE SEPARATOR - 0x2029 PARAGRAPH SEPARATOR
[InlineData(@"\u2028", (char)0x2028)]
[InlineData(@"\u2029", (char)0x2029)]
// Between #3 and #4
[InlineData(@"漢", (char)0x6F22)]
// #4. 0xD800 - 0xDFFF
[InlineData(@"\ud800", (char)0xd800)]
[InlineData(@"\udabc", (char)0xdabc)]
[InlineData(@"\udfff", (char)0xdfff)]
// Between #4 and #5
[InlineData("\ueabc", (char)0xeabc)]
// #5. 0xFFFE - 0xFFFF
[InlineData(@"\ufffe", (char)0xfffe)]
[InlineData(@"\uffff", (char)0xffff)]
public void Character(string expected, char value)
{
WriteInMultiplePositionsAndCheck(expected, value.ToString());
}
[Theory]
[InlineData(@"\ud83d\udc4d", "👍")]
public void String(string expected, string value)
{
WriteInMultiplePositionsAndCheck(expected, value);
}
private static void WriteInMultiplePositionsAndCheck(string expected, string value)
{
Assert.StrictEqual($"\"{expected}\"", WriteToString(value));
Assert.StrictEqual($"\"{expected}_after\"", WriteToString($"{value}_after"));
Assert.StrictEqual($"\"before_{expected}\"", WriteToString($"before_{value}"));
Assert.StrictEqual($"\"before_{expected}_after\"", WriteToString($"before_{value}_after"));
}
private static string WriteToString(Action<JsonWriter> action)
{
var stringWriter = new StringWriter();
using (var jsonWriter = new JsonWriter(stringWriter))
{
action(jsonWriter);
}
return stringWriter.ToString();
}
private static string WriteToString(bool value)
{
return WriteToString(j => j.Write(value));
}
private static string WriteToString(int value)
{
return WriteToString(j => j.Write(value));
}
private static string WriteToString(string value)
{
return WriteToString(j => j.Write(value));
}
}
}
......@@ -210,13 +210,13 @@ public bool ReportErrors(IEnumerable<Diagnostic> diagnostics, TextWriter console
// We want to report diagnostics with source suppression in the error log file.
// However, these diagnostics should not be reported on the console output.
errorLoggerOpt?.LogDiagnostic(diag, this.Culture);
errorLoggerOpt?.LogDiagnostic(diag);
if (diag.IsSuppressed)
{
continue;
}
consoleOutput.WriteLine(DiagnosticFormatter.Format(diag, this.Culture));
consoleOutput.WriteLine(DiagnosticFormatter.Format(diag));
if (diag.Severity == DiagnosticSeverity.Error)
{
......@@ -251,7 +251,7 @@ public bool ReportErrors(IEnumerable<DiagnosticInfo> diagnostics, TextWriter con
}
PrintError(diagnostic, consoleOutput);
errorLoggerOpt?.LogDiagnostic(Diagnostic.Create(diagnostic), this.Culture);
errorLoggerOpt?.LogDiagnostic(Diagnostic.Create(diagnostic));
if (diagnostic.Severity == DiagnosticSeverity.Error)
{
......@@ -278,7 +278,7 @@ public ErrorLogger GetErrorLogger(TextWriter consoleOutput, CancellationToken ca
return null;
}
return new ErrorLogger(errorLog, GetToolName(), GetAssemblyFileVersion(), GetAssemblyVersion());
return new ErrorLogger(errorLog, GetToolName(), GetAssemblyFileVersion(), GetAssemblyVersion(), Culture);
}
/// <summary>
......
......@@ -15,28 +15,32 @@ namespace Microsoft.CodeAnalysis
/// <summary>
/// Used for logging all compiler diagnostics into a given <see cref="Stream"/>.
/// This logger is responsible for closing the given stream on <see cref="Dispose"/>.
/// It is incorrect to use the logger concurrently from multiple threads.
///
/// The log format is SARIF (Static Analysis Results Interchange Format)
/// https://sarifweb.azurewebsites.net
/// https://github.com/sarif-standard/sarif-spec
/// </summary>
internal partial class ErrorLogger : IDisposable
{
// Internal for testing purposes.
internal const string OutputFormatVersion = "0.4";
private readonly JsonWriter _writer;
private readonly DiagnosticDescriptorSet _descriptors;
private readonly CultureInfo _culture;
public ErrorLogger(Stream stream, string toolName, string toolFileVersion, Version toolAssemblyVersion)
public ErrorLogger(Stream stream, string toolName, string toolFileVersion, Version toolAssemblyVersion, CultureInfo culture)
{
Debug.Assert(stream != null);
Debug.Assert(stream.Position == 0);
_writer = new JsonWriter(new StreamWriter(stream));
_descriptors = new DiagnosticDescriptorSet();
_culture = culture;
_writer.WriteObjectStart(); // root
_writer.Write("version", OutputFormatVersion);
_writer.WriteArrayStart("runLogs");
_writer.WriteObjectStart(); // runLog
_writer.Write("$schema", "http://json.schemastore.org/sarif-1.0.0-beta.5");
_writer.Write("version", "1.0.0-beta.5");
_writer.WriteArrayStart("runs");
_writer.WriteObjectStart(); // run
WriteToolInfo(toolName, toolFileVersion, toolAssemblyVersion);
......@@ -45,162 +49,227 @@ public ErrorLogger(Stream stream, string toolName, string toolFileVersion, Versi
private void WriteToolInfo(string name, string fileVersion, Version assemblyVersion)
{
_writer.WriteObjectStart("toolInfo");
_writer.WriteObjectStart("tool");
_writer.Write("name", name);
_writer.Write("version", assemblyVersion.ToString(fieldCount: 3));
_writer.Write("version", assemblyVersion.ToString());
_writer.Write("fileVersion", fileVersion);
_writer.Write("semanticVersion", assemblyVersion.ToString(fieldCount: 3));
_writer.WriteObjectEnd();
}
internal void LogDiagnostic(Diagnostic diagnostic, CultureInfo culture)
public void LogDiagnostic(Diagnostic diagnostic)
{
_writer.WriteObjectStart(); // result
_writer.Write("ruleId", diagnostic.Id);
_writer.Write("kind", GetKind(diagnostic.Severity));
WriteLocations(diagnostic.Location, diagnostic.AdditionalLocations);
string message = diagnostic.GetMessage(culture);
if (string.IsNullOrEmpty(message))
string ruleKey = _descriptors.Add(diagnostic.Descriptor);
if (ruleKey != diagnostic.Id)
{
message = "<None>";
_writer.Write("ruleKey", ruleKey);
}
string description = diagnostic.Descriptor.Description.ToString(culture);
if (string.IsNullOrEmpty(description))
_writer.Write("level", GetLevel(diagnostic.Severity));
string message = diagnostic.GetMessage(_culture);
if (!string.IsNullOrEmpty(message))
{
_writer.Write("fullMessage", message);
_writer.Write("message", message);
}
else
if (diagnostic.IsSuppressed)
{
_writer.Write("shortMessage", message);
_writer.Write("fullMessage", description);
_writer.WriteArrayStart("suppressionStates");
_writer.Write("suppressedInSource");
_writer.WriteArrayEnd();
}
_writer.Write("isSuppressedInSource", diagnostic.IsSuppressed);
WriteTags(diagnostic);
WriteProperties(diagnostic, culture);
WriteLocations(diagnostic.Location, diagnostic.AdditionalLocations);
WriteProperties(diagnostic);
_writer.WriteObjectEnd(); // result
}
private void WriteLocations(Location location, IReadOnlyList<Location> additionalLocations)
{
_writer.WriteArrayStart("locations");
if (HasPath(location))
{
_writer.WriteArrayStart("locations");
_writer.WriteObjectStart(); // location
_writer.WriteKey("resultFile");
WritePhysicalLocation(location);
WriteLocation(location);
_writer.WriteObjectEnd(); // location
_writer.WriteArrayEnd(); // locations
}
if (additionalLocations != null)
// See https://github.com/dotnet/roslyn/issues/11228 for discussion around
// whether this is the correct treatment of Diagnostic.AdditionalLocations
// as SARIF relatedLocations.
if (additionalLocations != null &&
additionalLocations.Count > 0 &&
additionalLocations.Any(l => HasPath(l)))
{
_writer.WriteArrayStart("relatedLocations");
foreach (var additionalLocation in additionalLocations)
{
WriteLocation(additionalLocation);
if (HasPath(additionalLocation))
{
_writer.WriteObjectStart(); // annotatedCodeLocation
_writer.WriteKey("physicalLocation");
WritePhysicalLocation(additionalLocation);
_writer.WriteObjectEnd(); // annotatedCodeLocation
}
}
}
_writer.WriteArrayEnd();
_writer.WriteArrayEnd(); // relatedLocations
}
}
private void WriteLocation(Location location)
private void WritePhysicalLocation(Location location)
{
if (location.SourceTree == null)
{
return;
}
Debug.Assert(HasPath(location));
_writer.WriteObjectStart(); // location
_writer.WriteArrayStart("analysisTarget");
_writer.WriteObjectStart(); // physical location component
FileLinePositionSpan span = location.GetLineSpan();
_writer.Write("uri", GetUri(location.SourceTree));
_writer.WriteObjectStart();
_writer.Write("uri", GetUri(span.Path));
// Note that SARIF lines and columns are 1-based, but FileLinePositionSpan is 0-based
FileLinePositionSpan span = location.GetLineSpan();
_writer.WriteKey("region");
_writer.WriteObjectStart();
_writer.WriteObjectStart("region");
_writer.Write("startLine", span.StartLinePosition.Line + 1);
_writer.Write("startColumn", span.StartLinePosition.Character + 1);
_writer.Write("endLine", span.EndLinePosition.Line + 1);
_writer.Write("endColumn", span.EndLinePosition.Character + 1);
_writer.WriteObjectEnd(); // region
_writer.WriteObjectEnd(); // physical location component
_writer.WriteArrayEnd(); // analysisTarget
_writer.WriteObjectEnd(); // location
_writer.WriteObjectEnd();
}
private static string GetUri(SyntaxTree syntaxTree)
private static bool HasPath(Location location)
{
return !string.IsNullOrEmpty(location.GetLineSpan().Path);
}
private static string GetUri(string path)
{
Debug.Assert(!string.IsNullOrEmpty(path));
Uri uri;
if (!Uri.TryCreate(syntaxTree.FilePath, UriKind.RelativeOrAbsolute, out uri))
if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out uri))
{
// The only constraint on SyntaxTree.FilePath is that it can be interpreted by
// The only constraint on paths are that they can be interpreted by
// various resolvers so there is no guarantee we can turn the arbitrary string
// in to a URI. If our attempt to do so fails, use the original string as the
// "URI".
return syntaxTree.FilePath;
return path;
}
return uri.ToString();
}
private void WriteTags(Diagnostic diagnostic)
private void WriteProperties(Diagnostic diagnostic)
{
if (diagnostic.CustomTags.Count > 0)
// Currently, the following are always inherited from the descriptor and therefore will be
// captured as rule metadata and need not be logged here. IsWarningAsError is also omitted
// because it can be inferred from level vs. defaultLevel in the log.
Debug.Assert(diagnostic.CustomTags.SequenceEqual(diagnostic.Descriptor.CustomTags));
Debug.Assert(diagnostic.Category == diagnostic.Descriptor.Category);
Debug.Assert(diagnostic.DefaultSeverity == diagnostic.Descriptor.DefaultSeverity);
Debug.Assert(diagnostic.IsEnabledByDefault == diagnostic.Descriptor.IsEnabledByDefault);
if (diagnostic.WarningLevel > 0 || diagnostic.Properties.Count > 0)
{
_writer.WriteArrayStart("tags");
_writer.WriteObjectStart("properties");
foreach (string tag in diagnostic.CustomTags)
if (diagnostic.WarningLevel > 0)
{
_writer.Write(tag);
_writer.Write("warningLevel", diagnostic.WarningLevel);
}
_writer.WriteArrayEnd();
if (diagnostic.Properties.Count > 0)
{
_writer.WriteObjectStart("customProperties");
foreach (var pair in diagnostic.Properties.OrderBy(x => x.Key, StringComparer.Ordinal))
{
_writer.Write(pair.Key, pair.Value);
}
_writer.WriteObjectEnd();
}
_writer.WriteObjectEnd(); // properties
}
}
private void WriteProperties(Diagnostic diagnostic, CultureInfo culture)
private void WriteRules()
{
_writer.WriteObjectStart("properties");
if (_descriptors.Count > 0)
{
_writer.WriteObjectStart("rules");
_writer.Write("severity", diagnostic.Severity.ToString());
foreach (var pair in _descriptors.ToSortedList())
{
DiagnosticDescriptor descriptor = pair.Value;
if (diagnostic.Severity == DiagnosticSeverity.Warning)
{
_writer.Write("warningLevel", diagnostic.WarningLevel.ToString());
}
_writer.WriteObjectStart(pair.Key); // rule
_writer.Write("id", descriptor.Id);
_writer.Write("defaultSeverity", diagnostic.DefaultSeverity.ToString());
string shortDescription = descriptor.Title.ToString(_culture);
if (!string.IsNullOrEmpty(shortDescription))
{
_writer.Write("shortDescription", shortDescription);
}
string title = diagnostic.Descriptor.Title.ToString(culture);
if (!string.IsNullOrEmpty(title))
{
_writer.Write("title", title);
}
string fullDescription = descriptor.Description.ToString(_culture);
if (!string.IsNullOrEmpty(fullDescription))
{
_writer.Write("fullDescription", fullDescription);
}
_writer.Write("category", diagnostic.Category);
_writer.Write("defaultLevel", GetLevel(descriptor.DefaultSeverity));
string helpLink = diagnostic.Descriptor.HelpLinkUri;
if (!string.IsNullOrEmpty(helpLink))
{
_writer.Write("helpLink", helpLink);
}
if (!string.IsNullOrEmpty(descriptor.HelpLinkUri))
{
_writer.Write("helpUri", descriptor.HelpLinkUri);
}
_writer.Write("isEnabledByDefault", diagnostic.IsEnabledByDefault.ToString());
_writer.WriteObjectStart("properties");
foreach (var pair in diagnostic.Properties.OrderBy(x => x.Key, StringComparer.Ordinal))
{
_writer.Write("customProperties." + pair.Key, pair.Value);
}
if (!string.IsNullOrEmpty(descriptor.Category))
{
_writer.Write("category", descriptor.Category);
}
_writer.Write("isEnabledByDefault", descriptor.IsEnabledByDefault);
if (descriptor.CustomTags.Any())
{
_writer.WriteArrayStart("tags");
foreach (string tag in descriptor.CustomTags)
{
_writer.Write(tag);
}
_writer.WriteArrayEnd(); // tags
}
_writer.WriteObjectEnd(); // properties
_writer.WriteObjectEnd(); // properties
_writer.WriteObjectEnd(); // rule
}
_writer.WriteObjectEnd(); // rules
}
}
private static string GetKind(DiagnosticSeverity severity)
private static string GetLevel(DiagnosticSeverity severity)
{
switch (severity)
{
......@@ -211,23 +280,108 @@ private static string GetKind(DiagnosticSeverity severity)
return "error";
case DiagnosticSeverity.Warning:
return "warning";
case DiagnosticSeverity.Hidden:
default:
// note that in the hidden or default cases, we still write out the actual severity as a
// property so no information is lost. We have to conform to the SARIF spec for kind,
// which allows only pass, warning, error, or notApplicable.
return "warning";
// hidden diagnostics are not reported on the command line and therefore not currently given to
// the error logger. We could represent it with a custom property in the SARIF log if that changes.
Debug.Assert(false);
goto case DiagnosticSeverity.Warning;
}
}
public void Dispose()
{
_writer.WriteArrayEnd(); // results
_writer.WriteObjectEnd(); // runLog
_writer.WriteArrayEnd(); // runLogs
WriteRules();
_writer.WriteObjectEnd(); // run
_writer.WriteArrayEnd(); // runs
_writer.WriteObjectEnd(); // root
_writer.Dispose();
}
/// <summary>
/// Represents a distinct set of <see cref="DiagnosticDescriptor"/>s and provides unique string keys
/// to distinguish them.
///
/// The first <see cref="DiagnosticDescriptor"/> added with a given <see cref="DiagnosticDescriptor.Id"/>
/// value is given that value as its unique key. Subsequent adds with the same ID will have .NNN
/// apppended to their with an auto-incremented numeric value.
/// </summary>
private sealed class DiagnosticDescriptorSet
{
// DiagnosticDescriptor.Id -> auto-incremented counter
private Dictionary<string, int> _counters = new Dictionary<string, int>();
// DiagnosticDescriptor -> unique key
private Dictionary<DiagnosticDescriptor, string> _keys = new Dictionary<DiagnosticDescriptor, string>();
/// <summary>
/// The total number of descriptors in the set.
/// </summary>
public int Count => _keys.Count;
/// <summary>
/// Adds a descriptor to the set if not already present.
/// </summary>
/// <returns>
/// The unique key assigned to the given descriptor.
/// </returns>
public string Add(DiagnosticDescriptor descriptor)
{
// Case 1: Descriptor has already been seen -> retrieve key from cache.
string key;
if (_keys.TryGetValue(descriptor, out key))
{
return key;
}
// Case 2: First time we see a decriptor with a given ID -> use its ID as the key.
int counter;
if (!_counters.TryGetValue(descriptor.Id, out counter))
{
_counters.Add(descriptor.Id, 0);
_keys.Add(descriptor, descriptor.Id);
return descriptor.Id;
}
// Case 3: We've already seen a different descriptor with the same ID -> generate a key.
//
// This will only need to loop in the corner case where there is an actual descriptor
// with non-generated ID=X.NNN and more than one descriptor with ID=X.
do
{
_counters[descriptor.Id] = ++counter;
key = descriptor.Id + "." + counter.ToString("000", CultureInfo.InvariantCulture);
} while (_counters.ContainsKey(key));
_keys.Add(descriptor, key);
return key;
}
/// <summary>
/// Converts the set to a list of (key, descriptor) pairs sorted by key.
/// </summary>
public List<KeyValuePair<string, DiagnosticDescriptor>> ToSortedList()
{
Debug.Assert(Count > 0);
var list = new List<KeyValuePair<string, DiagnosticDescriptor>>(Count);
foreach (var pair in _keys)
{
Debug.Assert(list.Capacity > list.Count);
list.Add(new KeyValuePair<string, DiagnosticDescriptor>(pair.Value, pair.Key));
}
Debug.Assert(list.Capacity == list.Count);
list.Sort((x, y) => string.CompareOrdinal(x.Key, y.Key));
return list;
}
}
}
}
......
// 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;
using System.Collections.Concurrent;
using System.Collections.Generic;
......@@ -11,7 +12,7 @@ namespace Roslyn.Utilities
/// A concurrent, simplified HashSet.
/// </summary>
[DebuggerDisplay("Count = {Count}")]
internal sealed class ConcurrentSet<T> : IEnumerable<T>
internal sealed class ConcurrentSet<T> : ICollection<T>
{
/// <summary>
/// The default concurrency level is 2. That means the collection can cope with up to two
......@@ -65,6 +66,8 @@ public bool IsEmpty
get { return _dictionary.IsEmpty; }
}
public bool IsReadOnly => false;
/// <summary>
/// Determine whether the given value is in the set.
/// </summary>
......@@ -85,6 +88,17 @@ public bool Add(T value)
return _dictionary.TryAdd(value, 0);
}
public void AddRange(IEnumerable<T> values)
{
if (values != null)
{
foreach (var v in values)
{
Add(v);
}
}
}
/// <summary>
/// Attempts to remove a value from the set.
/// </summary>
......@@ -161,5 +175,15 @@ IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumeratorImpl();
}
void ICollection<T>.Add(T item)
{
Add(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
}
}
......@@ -17,23 +17,21 @@ namespace Roslyn.Utilities
/// to balance array/object start/end, to only write key-value pairs to objects and
/// elements to arrays, etc.
///
/// Takes ownership of the given StreamWriter at construction and handles its disposal.
/// Takes ownership of the given <see cref="TextWriter" /> at construction and handles its disposal.
/// </summary>
internal sealed class JsonWriter : IDisposable
{
private readonly StreamWriter _output;
private readonly TextWriter _output;
private int _indent;
private string _pending;
private static readonly string s_newLine = Environment.NewLine;
private static readonly string s_commaNewLine = "," + Environment.NewLine;
private Pending _pending;
private enum Pending { None, NewLineAndIndent, CommaNewLineAndIndent };
private const string Indentation = " ";
public JsonWriter(StreamWriter output)
public JsonWriter(TextWriter output)
{
_output = output;
_pending = "";
_pending = Pending.None;
}
public void WriteObjectStart()
......@@ -72,7 +70,7 @@ public void WriteKey(string key)
{
Write(key);
_output.Write(": ");
_pending = "";
_pending = Pending.None;
}
public void Write(string key, string value)
......@@ -99,33 +97,41 @@ public void Write(string value)
_output.Write('"');
_output.Write(EscapeString(value));
_output.Write('"');
_pending = s_commaNewLine;
_pending = Pending.CommaNewLineAndIndent;
}
public void Write(int value)
{
WritePending();
_output.Write(value);
_pending = s_commaNewLine;
_output.Write(value.ToString(CultureInfo.InvariantCulture));
_pending = Pending.CommaNewLineAndIndent;
}
public void Write(bool value)
{
WritePending();
_output.Write(value ? "true" : "false");
_pending = s_commaNewLine;
_pending = Pending.CommaNewLineAndIndent;
}
private void WritePending()
{
if (_pending.Length > 0)
if (_pending == Pending.None)
{
_output.Write(_pending);
return;
}
for (int i = 0; i < _indent; i++)
{
_output.Write(Indentation);
}
Debug.Assert(_pending == Pending.NewLineAndIndent || _pending == Pending.CommaNewLineAndIndent);
if (_pending == Pending.CommaNewLineAndIndent)
{
_output.Write(',');
}
_output.WriteLine();
for (int i = 0; i < _indent; i++)
{
_output.Write(Indentation);
}
}
......@@ -133,17 +139,17 @@ private void WriteStart(char c)
{
WritePending();
_output.Write(c);
_pending = s_newLine;
_pending = Pending.NewLineAndIndent;
_indent++;
}
private void WriteEnd(char c)
{
_pending = s_newLine;
_pending = Pending.NewLineAndIndent;
_indent--;
WritePending();
_output.Write(c);
_pending = s_commaNewLine;
_pending = Pending.CommaNewLineAndIndent;
}
public void Dispose()
......@@ -156,13 +162,6 @@ public void Dispose()
//
// https://github.com/dotnet/corefx/blob/master/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JavaScriptString.cs
//
// Possible future improvements: https://github.com/dotnet/roslyn/issues/9769
//
// - Avoid intermediate StringBuilder and send escaped output directly to the destination.
//
// - Stop escaping '/': it is is optional per JSON spec and several users have expressed
// that they don't like the way '\/' looks.
//
private static string EscapeString(string value)
{
PooledStringBuilder pooledBuilder = null;
......@@ -179,7 +178,7 @@ private static string EscapeString(string value)
{
char c = value[i];
if (c == '\"' || c == '\'' || c == '/' || c == '\\' || ShouldAppendAsUnicode(c))
if (c == '\"' || c == '\\' || ShouldAppendAsUnicode(c))
{
if (b == null)
{
......@@ -205,12 +204,6 @@ private static string EscapeString(string value)
case '\\':
b.Append("\\\\");
break;
case '/':
b.Append("\\/");
break;
case '\'':
b.Append("\'");
break;
default:
if (ShouldAppendAsUnicode(c))
{
......
......@@ -94,60 +94,62 @@ End Class
""results"": [
{{
""ruleId"": ""BC42024"",
""kind"": ""warning"",
""level"": ""warning"",
""message"": ""Unused local variable: 'x'."",
""locations"": [
{{
""analysisTarget"": [
{{
""uri"": ""{0}"",
""region"": {{
""startLine"": 4,
""startColumn"": 13,
""endLine"": 4,
""endColumn"": 14
}}
""resultFile"": {{
""uri"": ""{0}"",
""region"": {{
""startLine"": 4,
""startColumn"": 13,
""endLine"": 4,
""endColumn"": 14
}}
]
}}
}}
],
""fullMessage"": ""Unused local variable: 'x'."",
""isSuppressedInSource"": false,
""tags"": [
""Compiler"",
""Telemetry""
],
""properties"": {{
""severity"": ""Warning"",
""warningLevel"": ""1"",
""defaultSeverity"": ""Warning"",
""title"": ""Unused local variable"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""warningLevel"": 1
}}
}},
{{
""ruleId"": ""BC30420"",
""kind"": ""error"",
""locations"": [
],
""fullMessage"": ""'Sub Main' was not found in '{1}'."",
""isSuppressedInSource"": false,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
],
""level"": ""error"",
""message"": ""'Sub Main' was not found in '{1}'.""
}}
],
""rules"": {{
""BC30420"": {{
""id"": ""BC30420"",
""defaultLevel"": ""error"",
""properties"": {{
""severity"": ""Error"",
""defaultSeverity"": ""Error"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
]
}}
}},
""BC42024"": {{
""id"": ""BC42024"",
""shortDescription"": ""Unused local variable"",
""defaultLevel"": ""warning"",
""properties"": {{
""category"": ""Compiler"",
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry""
]
}}
}}
]
}}
}}
]
}}", AnalyzerForErrorLogTest.GetEscapedUriForPath(sourceFilePath), Path.GetFileNameWithoutExtension(sourceFilePath))
}}", AnalyzerForErrorLogTest.GetUriForPath(sourceFilePath), Path.GetFileNameWithoutExtension(sourceFilePath))
Dim expectedText = expectedHeader + expectedIssues
Assert.Equal(expectedText, actualOutput)
......@@ -194,60 +196,65 @@ End Class
""results"": [
{{
""ruleId"": ""BC42024"",
""kind"": ""warning"",
""level"": ""warning"",
""message"": ""Unused local variable: 'x'."",
""suppressionStates"": [
""suppressedInSource""
],
""locations"": [
{{
""analysisTarget"": [
{{
""uri"": ""{0}"",
""region"": {{
""startLine"": 5,
""startColumn"": 13,
""endLine"": 5,
""endColumn"": 14
}}
""resultFile"": {{
""uri"": ""{0}"",
""region"": {{
""startLine"": 5,
""startColumn"": 13,
""endLine"": 5,
""endColumn"": 14
}}
]
}}
}}
],
""fullMessage"": ""Unused local variable: 'x'."",
""isSuppressedInSource"": true,
""tags"": [
""Compiler"",
""Telemetry""
],
""properties"": {{
""severity"": ""Warning"",
""warningLevel"": ""1"",
""defaultSeverity"": ""Warning"",
""title"": ""Unused local variable"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""warningLevel"": 1
}}
}},
{{
""ruleId"": ""BC30420"",
""kind"": ""error"",
""locations"": [
],
""fullMessage"": ""'Sub Main' was not found in '{1}'."",
""isSuppressedInSource"": false,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
],
""level"": ""error"",
""message"": ""'Sub Main' was not found in '{1}'.""
}}
],
""rules"": {{
""BC30420"": {{
""id"": ""BC30420"",
""defaultLevel"": ""error"",
""properties"": {{
""severity"": ""Error"",
""defaultSeverity"": ""Error"",
""category"": ""Compiler"",
""isEnabledByDefault"": ""True""
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry"",
""NotConfigurable""
]
}}
}},
""BC42024"": {{
""id"": ""BC42024"",
""shortDescription"": ""Unused local variable"",
""defaultLevel"": ""warning"",
""properties"": {{
""category"": ""Compiler"",
""isEnabledByDefault"": true,
""tags"": [
""Compiler"",
""Telemetry""
]
}}
}}
]
}}
}}
]
}}", AnalyzerForErrorLogTest.GetEscapedUriForPath(sourceFilePath), Path.GetFileNameWithoutExtension(sourceFilePath))
}}", AnalyzerForErrorLogTest.GetUriForPath(sourceFilePath), Path.GetFileNameWithoutExtension(sourceFilePath))
Dim expectedText = expectedHeader + expectedIssues
Assert.Equal(expectedText, actualOutput)
......
......@@ -12,9 +12,8 @@
namespace Microsoft.CodeAnalysis.Editor.CSharp.UseAutoProperty
{
// https://github.com/dotnet/roslyn/issues/5408
//[Export]
//[DiagnosticAnalyzer(LanguageNames.CSharp)]
[Export]
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class UseAutoPropertyAnalyzer : AbstractUseAutoPropertyAnalyzer<PropertyDeclarationSyntax, FieldDeclarationSyntax, VariableDeclaratorSyntax, ExpressionSyntax>
{
protected override bool SupportsReadOnlyProperties(Compilation compilation)
......
......@@ -1728,7 +1728,27 @@ void M(IEnumerable<string> args)
args = args.Select(a =>[|b|])
}
}";
await TestAsync(text, "System.Object", testPosition: false);
await TestAsync(text, "System.String", testPosition: false);
}
[Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
[WorkItem(1903, "https://github.com/dotnet/roslyn/issues/1903")]
public async Task TestSelectLambda3()
{
var text =
@"using System.Collections.Generic;
using System.Linq;
class A { }
class B { }
class C
{
IEnumerable<B> GetB(IEnumerable<A> a)
{
return a.Select(i => [|Foo(i)|]);
}
}";
await TestAsync(text, "global::B");
}
[Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
......
......@@ -63,9 +63,7 @@ private ISymbol FindSymbol()
// CancellationToken.None.
var semanticModel = Document.GetPartialSemanticModelAsync(CancellationToken.None).GetAwaiter().GetResult();
var root = semanticModel.SyntaxTree.GetRoot(CancellationToken.None);
var node = root.FindNode(_declaredSymbolInfo.Span);
return semanticModel.GetDeclaredSymbol(node, CancellationToken.None);
return _declaredSymbolInfo.Resolve(semanticModel, CancellationToken.None);
}
}
}
......
......@@ -20,8 +20,9 @@ internal static class NavigateToSymbolFinder
foreach (var document in project.Documents)
{
cancellationToken.ThrowIfCancellationRequested();
var declaredSymbolInfos = await document.GetDeclaredSymbolInfosAsync(cancellationToken).ConfigureAwait(false);
foreach (var declaredSymbolInfo in declaredSymbolInfos)
var declarationInfo = await document.GetDeclarationInfoAsync(cancellationToken).ConfigureAwait(false);
foreach (var declaredSymbolInfo in declarationInfo.DeclaredSymbolInfos)
{
cancellationToken.ThrowIfCancellationRequested();
var patternMatches = patternMatcher.GetMatches(
......
......@@ -126,7 +126,7 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Roslyn.VisualStudio.Services.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.VisualStudio.Services.UnitTests" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.CodeAnalysis.Editor" />
......
......@@ -8,9 +8,8 @@ Imports Microsoft.CodeAnalysis.UseAutoProperty
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UseAutoProperty
' https://github.com/dotnet/roslyn/issues/5408
'<Export>
'<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
<Export>
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class UseAutoPropertyAnalyzer
Inherits AbstractUseAutoPropertyAnalyzer(Of PropertyBlockSyntax, FieldDeclarationSyntax, ModifiedIdentifierSyntax, ExpressionSyntax)
......
......@@ -66,15 +66,23 @@ public override void Initialize(AnalysisContext context)
private static string GetExpectedPropertiesMapText()
{
var expectedText = "";
var expectedText = @"
""customProperties"": {";
foreach (var kvp in s_properties.OrderBy(kvp => kvp.Key))
{
expectedText += ",";
if (!expectedText.EndsWith("{"))
{
expectedText += ",";
}
expectedText += string.Format(@"
""customProperties.{0}"": ""{1}""", kvp.Key, kvp.Value);
""{0}"": ""{1}""", kvp.Key, kvp.Value);
}
expectedText += @"
}";
return expectedText;
}
......@@ -83,75 +91,78 @@ public static string GetExpectedErrorLogResultsText(Compilation compilation)
var tree = compilation.SyntaxTrees.First();
var root = tree.GetRoot();
var expectedLineSpan = root.GetLocation().GetLineSpan();
var filePath = GetEscapedUriForPath(tree.FilePath);
var filePath = GetUriForPath(tree.FilePath);
return @"
""results"": [
{
""ruleId"": """ + Descriptor1.Id + @""",
""kind"": """ + (Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
""level"": """ + (Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
""message"": """ + Descriptor1.MessageFormat + @""",
""locations"": [
{
""analysisTarget"": [
{
""uri"": """ + filePath + @""",
""region"": {
""startLine"": " + (expectedLineSpan.StartLinePosition.Line + 1) + @",
""startColumn"": " + (expectedLineSpan.StartLinePosition.Character + 1) + @",
""endLine"": " + (expectedLineSpan.EndLinePosition.Line + 1) + @",
""endColumn"": " + (expectedLineSpan.EndLinePosition.Character + 1) + @"
}
""resultFile"": {
""uri"": """ + filePath + @""",
""region"": {
""startLine"": " + (expectedLineSpan.StartLinePosition.Line + 1) + @",
""startColumn"": " + (expectedLineSpan.StartLinePosition.Character + 1) + @",
""endLine"": " + (expectedLineSpan.EndLinePosition.Line + 1) + @",
""endColumn"": " + (expectedLineSpan.EndLinePosition.Character + 1) + @"
}
]
}
}
],
""shortMessage"": """ + Descriptor1.MessageFormat + @""",
""fullMessage"": """ + Descriptor1.Description + @""",
""isSuppressedInSource"": false,
""tags"": [
" + String.Join("," + Environment.NewLine + " ", Descriptor1.CustomTags.Select(s => $"\"{s}\"")) + @"
],
""properties"": {
""severity"": """ + Descriptor1.DefaultSeverity + @""",
""warningLevel"": ""1"",
""defaultSeverity"": """ + Descriptor1.DefaultSeverity + @""",
""title"": """ + Descriptor1.Title + @""",
""category"": """ + Descriptor1.Category + @""",
""helpLink"": """ + Descriptor1.HelpLinkUri + @""",
""isEnabledByDefault"": """ + Descriptor1.IsEnabledByDefault + @"""" +
GetExpectedPropertiesMapText() + @"
""warningLevel"": 1," + GetExpectedPropertiesMapText() + @"
}
},
{
""ruleId"": """ + Descriptor2.Id + @""",
""kind"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
""locations"": [
],
""shortMessage"": """ + Descriptor2.MessageFormat + @""",
""fullMessage"": """ + Descriptor2.Description + @""",
""isSuppressedInSource"": false,
""tags"": [
" + String.Join("," + Environment.NewLine + " ", Descriptor2.CustomTags.Select(s => $"\"{s}\"")) + @"
],
""level"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
""message"": """ + Descriptor2.MessageFormat + @""",
""properties"": {" +
GetExpectedPropertiesMapText() + @"
}
}
],
""rules"": {
""" + Descriptor1.Id + @""": {
""id"": """ + Descriptor1.Id + @""",
""shortDescription"": """ + Descriptor1.Title + @""",
""fullDescription"": """ + Descriptor1.Description + @""",
""defaultLevel"": """ + (Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
""helpUri"": """ + Descriptor1.HelpLinkUri + @""",
""properties"": {
""category"": """ + Descriptor1.Category + @""",
""isEnabledByDefault"": " + (Descriptor2.IsEnabledByDefault ? "true" : "false") + @",
""tags"": [
" + String.Join("," + Environment.NewLine + " ", Descriptor1.CustomTags.Select(s => $"\"{s}\"")) + @"
]
}
},
""" + Descriptor2.Id + @""": {
""id"": """ + Descriptor2.Id + @""",
""shortDescription"": """ + Descriptor2.Title + @""",
""fullDescription"": """ + Descriptor2.Description + @""",
""defaultLevel"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
""helpUri"": """ + Descriptor2.HelpLinkUri + @""",
""properties"": {
""severity"": """ + Descriptor2.DefaultSeverity + @""",
""defaultSeverity"": """ + Descriptor2.DefaultSeverity + @""",
""title"": """ + Descriptor2.Title + @""",
""category"": """ + Descriptor2.Category + @""",
""helpLink"": """ + Descriptor2.HelpLinkUri + @""",
""isEnabledByDefault"": """ + Descriptor2.IsEnabledByDefault + @"""" +
GetExpectedPropertiesMapText() + @"
""isEnabledByDefault"": " + (Descriptor2.IsEnabledByDefault ? "true" : "false") + @",
""tags"": [
" + String.Join("," + Environment.NewLine + " ", Descriptor2.CustomTags.Select(s => $"\"{s}\"")) + @"
]
}
}
]
}
}
]
}";
}
public static string GetEscapedUriForPath(string path)
public static string GetUriForPath(string path)
{
return new Uri(path, UriKind.RelativeOrAbsolute).ToString().Replace("/", "\\/");
return new Uri(path, UriKind.RelativeOrAbsolute).ToString();
}
}
......
......@@ -305,18 +305,21 @@ public static AnalyzerReference GetCompilerDiagnosticAnalyzerReference(string la
internal static string GetExpectedErrorLogHeader(string actualOutput, CommonCompiler compiler)
{
var expectedToolName = compiler.GetToolName();
var expectedProductVersion = compiler.GetAssemblyVersion().ToString(fieldCount: 3);
var expectedVersion = compiler.GetAssemblyVersion();
var expectedSemanticVersion = compiler.GetAssemblyVersion().ToString(fieldCount: 3);
var expectedFileVersion = compiler.GetAssemblyFileVersion();
return string.Format(@"{{
""version"": ""{0}"",
""runLogs"": [
""$schema"": ""http://json.schemastore.org/sarif-1.0.0-beta.5"",
""version"": ""1.0.0-beta.5"",
""runs"": [
{{
""toolInfo"": {{
""name"": ""{1}"",
""version"": ""{2}"",
""fileVersion"": ""{3}""
}},", ErrorLogger.OutputFormatVersion, expectedToolName, expectedProductVersion, expectedFileVersion);
""tool"": {{
""name"": ""{0}"",
""version"": ""{1}"",
""fileVersion"": ""{2}"",
""semanticVersion"": ""{3}""
}},", expectedToolName, expectedVersion, expectedFileVersion, expectedSemanticVersion);
}
public static string Stringize(this Diagnostic e)
......
......@@ -25,7 +25,7 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
if (namedType.TypeKind == TypeKind.Class)
{
var derivedTypes = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(namedType, solution, cancellationToken).ConfigureAwait(false);
var derivedTypes = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(namedType, solution, cancellationToken).ConfigureAwait(false);
foreach (var derivedType in derivedTypes)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(derivedType, relatedNode: node).ConfigureAwait(false);
......@@ -34,7 +34,8 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
}
else if (namedType.TypeKind == TypeKind.Interface)
{
var derivedTypes = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(namedType, solution, cancellationToken).ConfigureAwait(false);
var derivedTypes = await DependentTypeFinder.FindImmediatelyDerivedAndImplementingTypesAsync(
namedType, solution, cancellationToken).ConfigureAwait(false);
foreach (var derivedType in derivedTypes)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(derivedType, relatedNode: node).ConfigureAwait(false);
......
......@@ -58,9 +58,6 @@ internal abstract class VisualStudioWorkspaceImpl : VisualStudioWorkspace
private readonly ForegroundThreadAffinitizedObject _foregroundObject = new ForegroundThreadAffinitizedObject();
private PackageInstallerService _packageInstallerService;
private SymbolSearchService _symbolSearchService;
public VisualStudioWorkspaceImpl(
SVsServiceProvider serviceProvider,
WorkspaceBackgroundWork backgroundWork)
......@@ -113,11 +110,6 @@ protected void InitializeStandardVisualStudioWorkspace(SVsServiceProvider servic
// Ensure the options factory services are initialized on the UI thread
this.Services.GetService<IOptionService>();
// Ensure the nuget package services are initialized on the UI thread.
_symbolSearchService = this.Services.GetService<ISymbolSearchService>() as SymbolSearchService;
_packageInstallerService = (PackageInstallerService)this.Services.GetService<IPackageInstallerService>();
_packageInstallerService.Connect(this);
}
/// <summary>NOTE: Call only from derived class constructor</summary>
......@@ -1021,9 +1013,6 @@ internal void StopSolutionCrawler()
protected override void Dispose(bool finalize)
{
_packageInstallerService?.Disconnect(this);
_symbolSearchService?.Dispose();
// workspace is going away. unregister this workspace from work coordinator
StopSolutionCrawler();
......
......@@ -13,6 +13,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Packaging;
......@@ -41,13 +42,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Packaging
internal partial class PackageInstallerService : ForegroundThreadAffinitizedObject, IPackageInstallerService, IVsSearchProviderCallback
{
private readonly object _gate = new object();
private readonly VisualStudioWorkspaceImpl _workspace;
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService;
/// <summary>
/// The workspace we're connected to. When we're disconnected this will become 'null'.
/// That's our signal to stop working.
/// </summary>
private VisualStudioWorkspaceImpl _workspace;
private IVsPackageInstallerServices _packageInstallerServices;
private IVsPackageInstaller _packageInstaller;
private IVsPackageUninstaller _packageUninstaller;
......@@ -66,8 +63,10 @@ internal partial class PackageInstallerService : ForegroundThreadAffinitizedObje
[ImportingConstructor]
public PackageInstallerService(
VisualStudioWorkspaceImpl workspace,
IVsEditorAdaptersFactoryService editorAdaptersFactoryService)
{
_workspace = workspace;
_editorAdaptersFactoryService = editorAdaptersFactoryService;
}
......@@ -75,25 +74,25 @@ internal partial class PackageInstallerService : ForegroundThreadAffinitizedObje
public event EventHandler PackageSourcesChanged;
internal void Connect(VisualStudioWorkspaceImpl workspace)
internal void Start()
{
this.AssertIsForeground();
var options = workspace.Options;
var options = _workspace.Options;
if (!options.GetOption(ServiceComponentOnOffOptions.SymbolSearch))
{
return;
}
ConnectWorker(workspace);
StartWorker();
}
// Don't inline this method. The references to nuget types will cause the nuget packages
// to load.
[MethodImpl(MethodImplOptions.NoInlining)]
private void ConnectWorker(VisualStudioWorkspaceImpl workspace)
private void StartWorker()
{
var componentModel = workspace.GetVsService<SComponentModel, IComponentModel>();
var componentModel = _workspace.GetVsService<SComponentModel, IComponentModel>();
_packageInstallerServices = componentModel.GetExtensions<IVsPackageInstallerServices>().FirstOrDefault();
_packageInstaller = componentModel.GetExtensions<IVsPackageInstaller>().FirstOrDefault();
_packageUninstaller = componentModel.GetExtensions<IVsPackageUninstaller>().FirstOrDefault();
......@@ -105,7 +104,6 @@ private void ConnectWorker(VisualStudioWorkspaceImpl workspace)
}
// Start listening to workspace changes.
_workspace = workspace;
_workspace.WorkspaceChanged += OnWorkspaceChanged;
_packageSourceProvider.SourcesChanged += OnSourceProviderSourcesChanged;
......@@ -118,7 +116,7 @@ private void ConnectWorker(VisualStudioWorkspaceImpl workspace)
_packageUninstaller != null &&
_packageSourceProvider != null;
internal void Disconnect(VisualStudioWorkspaceImpl workspace)
internal void Stop()
{
this.AssertIsForeground();
......@@ -127,11 +125,8 @@ internal void Disconnect(VisualStudioWorkspaceImpl workspace)
return;
}
Debug.Assert(workspace == _workspace);
_packageSourceProvider.SourcesChanged -= OnSourceProviderSourcesChanged;
_workspace.WorkspaceChanged -= OnWorkspaceChanged;
_workspace = null;
}
private void OnSourceProviderSourcesChanged(object sender, EventArgs e)
......
......@@ -10,20 +10,24 @@
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.CodeAnalysis.Versions;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.Implementation;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interactive;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.FindResults;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.RuleSets;
using Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource;
using Microsoft.VisualStudio.LanguageServices.Packaging;
using Microsoft.VisualStudio.LanguageServices.SymbolSearch;
using Microsoft.VisualStudio.LanguageServices.Utilities;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Task = System.Threading.Tasks.Task;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interactive;
using static Microsoft.CodeAnalysis.Utilities.ForegroundThreadDataKind;
using Task = System.Threading.Tasks.Task;
namespace Microsoft.VisualStudio.LanguageServices.Setup
{
......@@ -40,6 +44,9 @@ internal class RoslynPackage : Package
private RuleSetEventHandler _ruleSetEventHandler;
private IDisposable _solutionEventMonitor;
private PackageInstallerService _packageInstallerService;
private SymbolSearchService _symbolSearchService;
protected override void Initialize()
{
base.Initialize();
......@@ -161,6 +168,14 @@ private void LoadComponents()
LoadAnalyzerNodeComponents();
// Ensure the nuget package services are initialized after we've loaded
// the solution.
_packageInstallerService = _workspace.Services.GetService<IPackageInstallerService>() as PackageInstallerService;
_symbolSearchService = _workspace.Services.GetService<ISymbolSearchService>() as SymbolSearchService;
_packageInstallerService?.Start();
_symbolSearchService?.Start();
Task.Run(() => LoadComponentsBackground());
}
......@@ -202,6 +217,9 @@ internal IComponentModel ComponentModel
protected override void Dispose(bool disposing)
{
_packageInstallerService?.Stop();
_symbolSearchService?.Stop();
UnregisterFindResultsLibraryManager();
DisposeVisualStudioDocumentTrackingService();
......
......@@ -170,7 +170,6 @@
<Compile Include="SymbolSearch\SymbolSearchService.IOService.cs" />
<Compile Include="SymbolSearch\SymbolSearchService.LogService.cs" />
<Compile Include="SymbolSearch\SymbolSearchService.RemoteControlService.cs" />
<Compile Include="SymbolSearch\SymbolSearchServiceFactory.cs" />
<Compile Include="SymbolSearch\Patching\Delta.cs" />
<Compile Include="RoslynPackage.cs" />
<Compile Include="ServicesVSResources.Designer.cs">
......
......@@ -5,22 +5,21 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Elfie.Model;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.Internal.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
using static System.FormattableString;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Options;
using System.Linq;
using System.Collections.Immutable;
namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch
{
......@@ -51,8 +50,6 @@ internal partial class SymbolSearchService
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly CancellationToken _cancellationToken;
private readonly Workspace _workspace;
private readonly ConcurrentDictionary<string, object> _sourceToUpdateSentinel =
new ConcurrentDictionary<string, object>();
......@@ -68,12 +65,6 @@ internal partial class SymbolSearchService
private readonly string _localSettingsDirectory;
private readonly Func<Exception, bool> _reportAndSwallowException;
public void Dispose()
{
// Cancel any existing work.
_cancellationTokenSource.Cancel();
}
private void LogInfo(string text) => _logService.LogInfo(text);
private void LogException(Exception e, string text) => _logService.LogException(e, text);
......
......@@ -3,7 +3,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
......@@ -12,14 +12,16 @@
using Microsoft.CodeAnalysis.Elfie.Model.Structures;
using Microsoft.CodeAnalysis.Elfie.Model.Tree;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.Internal.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Settings;
using static System.FormattableString;
using VSShell = Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch
......@@ -31,20 +33,22 @@ namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch
/// This implementation also spawns a task which will attempt to keep that database up to
/// date by downloading patches on a daily basis.
/// </summary>
internal partial class SymbolSearchService :
ForegroundThreadAffinitizedObject,
ISymbolSearchService,
IDisposable
[ExportWorkspaceService(typeof(ISymbolSearchService)), Shared]
internal partial class SymbolSearchService : ForegroundThreadAffinitizedObject, ISymbolSearchService
{
private readonly Workspace _workspace;
private ConcurrentDictionary<string, IAddReferenceDatabaseWrapper> _sourceToDatabase =
new ConcurrentDictionary<string, IAddReferenceDatabaseWrapper>();
private bool _started;
[ImportingConstructor]
public SymbolSearchService(
VSShell.SVsServiceProvider serviceProvider,
Workspace workspace,
IPackageInstallerService installerService)
: this(workspace,
installerService,
VisualStudioWorkspaceImpl workspace,
VSShell.SVsServiceProvider serviceProvider)
: this(workspace,
workspace.Services.GetService<IPackageInstallerService>(),
CreateRemoteControlService(serviceProvider),
new LogService((IVsActivityLog)serviceProvider.GetService(typeof(SVsActivityLog))),
new DelayService(),
......@@ -56,11 +60,6 @@ internal partial class SymbolSearchService :
FatalError.ReportWithoutCrash,
new CancellationTokenSource())
{
installerService.PackageSourcesChanged += OnOptionChanged;
var optionsService = workspace.Services.GetService<IOptionService>();
optionsService.OptionChanged += OnOptionChanged;
OnOptionChanged(this, EventArgs.Empty);
}
private static IRemoteControlService CreateRemoteControlService(VSShell.SVsServiceProvider serviceProvider)
......@@ -112,6 +111,38 @@ private static IRemoteControlService CreateRemoteControlService(VSShell.SVsServi
_cancellationToken = _cancellationTokenSource.Token;
}
internal void Start()
{
var options = _workspace.Options;
if (!options.GetOption(ServiceComponentOnOffOptions.SymbolSearch))
{
return;
}
var optionsService = _workspace.Services.GetService<IOptionService>();
optionsService.OptionChanged += OnOptionChanged;
// Start the whole process once we're connected
_installerService.PackageSourcesChanged += OnOptionChanged;
OnOptionChanged(this, EventArgs.Empty);
_started = true;
}
internal void Stop()
{
if (!_started)
{
return;
}
var optionsService = _workspace.Services.GetService<IOptionService>();
optionsService.OptionChanged -= OnOptionChanged;
_installerService.PackageSourcesChanged -= OnOptionChanged;
// Cancel any existing work.
_cancellationTokenSource.Cancel();
}
public IEnumerable<PackageWithTypeResult> FindPackagesWithType(
string source, string name, int arity, CancellationToken cancellationToken)
{
......
// 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.Composition;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.SymbolSearch;
using Roslyn.Utilities;
using VSShell = Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch
{
[ExportWorkspaceServiceFactory(typeof(ISymbolSearchService), WorkspaceKind.Host), Shared]
internal class SymbolSearchServiceFactory : IWorkspaceServiceFactory
{
private readonly VSShell.SVsServiceProvider _serviceProvider;
[ImportingConstructor]
public SymbolSearchServiceFactory(
VSShell.SVsServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
{
var options = workspaceServices.Workspace.Options;
if (options.GetOption(ServiceComponentOnOffOptions.SymbolSearch))
{
// Only support package search in vs workspace.
if (workspaceServices.Workspace is VisualStudioWorkspace)
{
return new SymbolSearchService(
_serviceProvider, workspaceServices.Workspace,
workspaceServices.GetService<IPackageInstallerService>());
}
}
return new NullSymbolSearchService();
}
private class NullSymbolSearchService : ISymbolSearchService
{
public IEnumerable<PackageWithTypeResult> FindPackagesWithType(
string source, string name, int arity, CancellationToken cancellationToken)
{
return SpecializedCollections.EmptyEnumerable<PackageWithTypeResult>();
}
public IEnumerable<ReferenceAssemblyWithTypeResult> FindReferenceAssembliesWithType(
string name, int arity, CancellationToken cancellationToken)
{
return SpecializedCollections.EmptyEnumerable<ReferenceAssemblyWithTypeResult>();
}
}
}
}
......@@ -52,6 +52,8 @@ public static string ActiveTextViewContents
}
}
public static IWpfTextViewMargin GetTextViewMargin(string marginName) => InvokeOnUIThread(() => ActiveTextViewHost.GetTextViewMargin(marginName));
public static ReadOnlyCollection<ICompletionSession> ActiveTextViewCompletionSessions => CompletionBroker.GetSessions(ActiveTextView);
public static IComponentModel ComponentModel => GetGlobalService<IComponentModel>(typeof(SComponentModel));
......@@ -155,9 +157,9 @@ public static void CleanupWorkspace()
private static T GetGlobalService<T>(Type serviceType) => InvokeOnUIThread(() => (T)(GlobalServiceProvider.GetService(serviceType)));
private static void InvokeOnUIThread(Action action) => CurrentApplicationDispatcher.Invoke(action);
public static void InvokeOnUIThread(Action action) => CurrentApplicationDispatcher.Invoke(action);
private static T InvokeOnUIThread<T>(Func<T> action) => CurrentApplicationDispatcher.Invoke(action);
public static T InvokeOnUIThread<T>(Func<T> action) => CurrentApplicationDispatcher.Invoke(action);
private static void LoadRoslynPackage()
{
......
......@@ -49,6 +49,7 @@
<Compile Include="Workspace\Workspace.cs" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.TestImpact.VisualStudio.IntegrationTests" />
<InternalsVisibleToTest Include="Roslyn.VisualStudio.Test.Setup" />
</ItemGroup>
<ItemGroup>
......@@ -128,4 +129,4 @@
</ProjectReference>
</ItemGroup>
<Import Project="..\..\..\build\Targets\VSL.Imports.targets" />
</Project>
\ No newline at end of file
</Project>
......@@ -39,6 +39,21 @@ public Solution CreateSolution(string solutionName, bool saveExistingSolutionIfE
return _solution;
}
public Solution OpenSolution(string path, bool saveExistingSolutionIfExists = false)
{
var dteSolution = IntegrationHelper.RetryRpcCall(() => _visualStudio.Dte.Solution);
if (IntegrationHelper.RetryRpcCall(() => dteSolution.IsOpen))
{
CloseSolution(saveExistingSolutionIfExists);
}
IntegrationHelper.RetryRpcCall(() => dteSolution.Open(path));
_solution = new Solution((Solution2)(dteSolution), path);
return _solution;
}
public void CloseSolution(bool saveFirst = false) => IntegrationHelper.RetryRpcCall(() => _visualStudio.Dte.Solution.Close(saveFirst));
}
}
// 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.Threading.Tasks;
using DteProject = EnvDTE.Project;
namespace Roslyn.VisualStudio.Test.Utilities
......@@ -35,5 +35,7 @@ internal Project(DteProject dteProject, Solution solution, ProjectLanguage langu
public ProjectLanguage Language => _language;
public Solution Solution => _solution;
public Task OpenFileAsync(string fileName) => DteProject.DTE.ExecuteCommandAsync("File.OpenFile", fileName);
}
}
......@@ -68,20 +68,33 @@ public Project AddProject(string projectName, ProjectTemplate projectTemplate, P
if (dteProject == null)
{
var dteSolutionProjects = IntegrationHelper.RetryRpcCall(() => _dteSolution.Projects);
dteProject = GetDteProject(projectName);
}
foreach (DteProject project in dteSolutionProjects)
{
var dteProjectName = IntegrationHelper.RetryRpcCall(() => project.Name);
return new Project(dteProject, this, projectLanguage);
}
public Project GetProject(string projectName, ProjectLanguage projectLanguage)
{
DteProject dteProject = GetDteProject(projectName);
return new Project(dteProject, this, projectLanguage);
}
private DteProject GetDteProject(string projectName)
{
var dteSolutionProjects = IntegrationHelper.RetryRpcCall(() => _dteSolution.Projects);
if (dteProjectName == projectName)
{
dteProject = project;
}
foreach (DteProject project in dteSolutionProjects)
{
var dteProjectName = IntegrationHelper.RetryRpcCall(() => project.Name);
if (dteProjectName == projectName)
{
return project;
}
}
return new Project(dteProject, this, projectLanguage);
throw new Exception($"The specified project could not be found. Project name: '{projectName}'");
}
public void Save()
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using System.Linq;
......@@ -20,7 +21,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
[ExportLanguageService(typeof(ISyntaxFactsService), LanguageNames.CSharp), Shared]
internal class CSharpSyntaxFactsService : ISyntaxFactsService
internal class CSharpSyntaxFactsService : AbstractSyntaxFactsService, ISyntaxFactsService
{
public bool IsAwaitKeyword(SyntaxToken token)
{
......@@ -747,7 +748,8 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
declaredSymbolInfo = new DeclaredSymbolInfo(classDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Class, classDecl.Identifier.Span);
DeclaredSymbolInfoKind.Class, classDecl.Identifier.Span,
GetInheritanceNames(classDecl.BaseList));
return true;
case SyntaxKind.ConstructorDeclaration:
var ctorDecl = (ConstructorDeclarationSyntax)node;
......@@ -757,6 +759,7 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Constructor,
ctorDecl.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty,
parameterCount: (ushort)(ctorDecl.ParameterList?.Parameters.Count ?? 0));
return true;
case SyntaxKind.DelegateDeclaration:
......@@ -764,42 +767,48 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
declaredSymbolInfo = new DeclaredSymbolInfo(delegateDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Delegate, delegateDecl.Identifier.Span);
DeclaredSymbolInfoKind.Delegate, delegateDecl.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty);
return true;
case SyntaxKind.EnumDeclaration:
var enumDecl = (EnumDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(enumDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Enum, enumDecl.Identifier.Span);
DeclaredSymbolInfoKind.Enum, enumDecl.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty);
return true;
case SyntaxKind.EnumMemberDeclaration:
var enumMember = (EnumMemberDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(enumMember.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.EnumMember, enumMember.Identifier.Span);
DeclaredSymbolInfoKind.EnumMember, enumMember.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty);
return true;
case SyntaxKind.EventDeclaration:
var eventDecl = (EventDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(ExpandExplicitInterfaceName(eventDecl.Identifier.ValueText, eventDecl.ExplicitInterfaceSpecifier),
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Event, eventDecl.Identifier.Span);
DeclaredSymbolInfoKind.Event, eventDecl.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty);
return true;
case SyntaxKind.IndexerDeclaration:
var indexerDecl = (IndexerDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(WellKnownMemberNames.Indexer,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Indexer, indexerDecl.ThisKeyword.Span);
DeclaredSymbolInfoKind.Indexer, indexerDecl.ThisKeyword.Span,
inheritanceNames: ImmutableArray<string>.Empty);
return true;
case SyntaxKind.InterfaceDeclaration:
var interfaceDecl = (InterfaceDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(interfaceDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Interface, interfaceDecl.Identifier.Span);
DeclaredSymbolInfoKind.Interface, interfaceDecl.Identifier.Span,
GetInheritanceNames(interfaceDecl.BaseList));
return true;
case SyntaxKind.MethodDeclaration:
var method = (MethodDeclarationSyntax)node;
......@@ -809,6 +818,7 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Method,
method.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty,
parameterCount: (ushort)(method.ParameterList?.Parameters.Count ?? 0),
typeParameterCount: (ushort)(method.TypeParameterList?.Parameters.Count ?? 0));
return true;
......@@ -817,14 +827,16 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
declaredSymbolInfo = new DeclaredSymbolInfo(ExpandExplicitInterfaceName(property.Identifier.ValueText, property.ExplicitInterfaceSpecifier),
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Property, property.Identifier.Span);
DeclaredSymbolInfoKind.Property, property.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty);
return true;
case SyntaxKind.StructDeclaration:
var structDecl = (StructDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(structDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Struct, structDecl.Identifier.Span);
DeclaredSymbolInfoKind.Struct, structDecl.Identifier.Span,
GetInheritanceNames(structDecl.BaseList));
return true;
case SyntaxKind.VariableDeclarator:
// could either be part of a field declaration or an event field declaration
......@@ -839,10 +851,12 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
? DeclaredSymbolInfoKind.Constant
: DeclaredSymbolInfoKind.Field;
declaredSymbolInfo = new DeclaredSymbolInfo(variableDeclarator.Identifier.ValueText,
GetContainerDisplayName(fieldDeclaration.Parent),
GetFullyQualifiedContainerName(fieldDeclaration.Parent),
kind, variableDeclarator.Identifier.Span);
declaredSymbolInfo = new DeclaredSymbolInfo(
variableDeclarator.Identifier.ValueText,
GetContainerDisplayName(fieldDeclaration.Parent),
GetFullyQualifiedContainerName(fieldDeclaration.Parent),
kind, variableDeclarator.Identifier.Span,
inheritanceNames: ImmutableArray<string>.Empty);
return true;
}
......@@ -853,6 +867,144 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
return false;
}
private ImmutableArray<string> GetInheritanceNames(BaseListSyntax baseList)
{
if (baseList == null)
{
return ImmutableArray<string>.Empty;
}
var builder = ImmutableArray.CreateBuilder<string>(baseList.Types.Count);
// It's not sufficient to just store the textual names we see in the inheritance list
// of a type. For example if we have:
//
// using Y = X;
// ...
// using Z = Y;
// ...
// class C : Z
//
// It's insufficient to just state that 'C' derives from 'Z'. If we search for derived
// types from 'B' we won't examine 'C'. To solve this, we keep track of the aliasing
// that occurs in containing scopes. Then, when we're adding an inheritance name we
// walk the alias maps and we also add any names that these names alias to. In the
// above example we'd put Z, Y, and X in the inheritance names list for 'C'.
// Each dictionary in this list is a mapping from alias name to the name of the thing
// it aliases. Then, each scope with alias mapping gets its own entry in this list.
// For the above example, we would produce: [{Z => Y}, {Y => X}]
var aliasMaps = AllocateAliasMapList();
try
{
AddAliasMaps(baseList, aliasMaps);
foreach (var baseType in baseList.Types)
{
AddInheritanceName(builder, baseType.Type, aliasMaps);
}
return builder.ToImmutable();
}
finally
{
FreeAliasMapList(aliasMaps);
}
}
private void AddAliasMaps(SyntaxNode node, List<Dictionary<string, string>> aliasMaps)
{
for (var current = node; current != null; current = current.Parent)
{
if (current.IsKind(SyntaxKind.NamespaceDeclaration))
{
ProcessUsings(aliasMaps, ((NamespaceDeclarationSyntax)current).Usings);
}
else if (current.IsKind(SyntaxKind.CompilationUnit))
{
ProcessUsings(aliasMaps, ((CompilationUnitSyntax)current).Usings);
}
}
}
private void ProcessUsings(List<Dictionary<string, string>> aliasMaps, SyntaxList<UsingDirectiveSyntax> usings)
{
Dictionary<string, string> aliasMap = null;
foreach (var usingDecl in usings)
{
if (usingDecl.Alias != null)
{
var mappedName = GetTypeName(usingDecl.Name);
if (mappedName != null)
{
aliasMap = aliasMap ?? AllocateAliasMap();
// If we have: using X = Foo, then we store a mapping from X -> Foo
// here. That way if we see a class that inherits from X we also state
// that it inherits from Foo as well.
aliasMap[usingDecl.Alias.Name.Identifier.ValueText] = mappedName;
}
}
}
if (aliasMap != null)
{
aliasMaps.Add(aliasMap);
}
}
private void AddInheritanceName(
ImmutableArray<string>.Builder builder, TypeSyntax type,
List<Dictionary<string, string>> aliasMaps)
{
var name = GetTypeName(type);
if (name != null)
{
// First, add the name that the typename that the type directly says it inherits from.
builder.Add(name);
// Now, walk the alias chain and add any names this alias may eventually map to.
var currentName = name;
foreach (var aliasMap in aliasMaps)
{
string mappedName;
if (aliasMap.TryGetValue(currentName, out mappedName))
{
// Looks like this could be an alias. Also include the name the alias points to
builder.Add(mappedName);
// Keep on searching. An alias in an inner namespcae can refer to an
// alias in an outer namespace.
currentName = mappedName;
}
}
}
}
private string GetTypeName(TypeSyntax type)
{
if (type is SimpleNameSyntax)
{
return GetSimpleTypeName((SimpleNameSyntax)type);
}
else if (type is QualifiedNameSyntax)
{
return GetSimpleTypeName(((QualifiedNameSyntax)type).Right);
}
else if (type is AliasQualifiedNameSyntax)
{
return GetSimpleTypeName(((AliasQualifiedNameSyntax)type).Name);
}
return null;
}
private static string GetSimpleTypeName(SimpleNameSyntax name)
{
return name.Identifier.ValueText;
}
private static string ExpandExplicitInterfaceName(string identifier, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier)
{
if (explicitInterfaceSpecifier == null)
......
......@@ -552,7 +552,7 @@ private IMethodSymbol Instantiate(IMethodSymbol method, IList<ITypeSymbol> invoc
// If the method has already been constructed poorly (i.e. with error types for type
// arguments), then unconstruct it.
if (method.TypeArguments.All(t => t.Kind == SymbolKind.ErrorType))
if (method.TypeArguments.Any(t => t.Kind == SymbolKind.ErrorType))
{
method = method.ConstructedFrom;
}
......
// 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.Immutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Text;
......@@ -35,7 +38,19 @@ internal struct DeclaredSymbolInfo
public ushort ParameterCount { get; }
public ushort TypeParameterCount { get; }
public DeclaredSymbolInfo(string name, string containerDisplayName, string fullyQualifiedContainerName, DeclaredSymbolInfoKind kind, TextSpan span, ushort parameterCount = 0, ushort typeParameterCount = 0)
/// <summary>
/// The names directly referenced in source that this type inherits from.
/// </summary>
public ImmutableArray<string> InheritanceNames { get; }
public DeclaredSymbolInfo(
string name,
string containerDisplayName,
string fullyQualifiedContainerName,
DeclaredSymbolInfoKind kind,
TextSpan span,
ImmutableArray<string> inheritanceNames,
ushort parameterCount = 0, ushort typeParameterCount = 0)
: this()
{
Name = name;
......@@ -45,6 +60,7 @@ public DeclaredSymbolInfo(string name, string containerDisplayName, string fully
Span = span;
ParameterCount = parameterCount;
TypeParameterCount = typeParameterCount;
InheritanceNames = inheritanceNames;
}
internal void WriteTo(ObjectWriter writer)
......@@ -57,6 +73,12 @@ internal void WriteTo(ObjectWriter writer)
writer.WriteInt32(Span.Length);
writer.WriteUInt16(ParameterCount);
writer.WriteUInt16(TypeParameterCount);
writer.WriteInt32(InheritanceNames.Length);
foreach (var name in InheritanceNames)
{
writer.WriteString(name);
}
}
internal static DeclaredSymbolInfo ReadFrom(ObjectReader reader)
......@@ -72,12 +94,35 @@ internal static DeclaredSymbolInfo ReadFrom(ObjectReader reader)
var parameterCount = reader.ReadUInt16();
var typeParameterCount = reader.ReadUInt16();
return new DeclaredSymbolInfo(name, immediateContainer, entireContainer, kind, new TextSpan(spanStart, spanLength), parameterCount, typeParameterCount);
var inheritanceNamesLength = reader.ReadInt32();
var builder = ImmutableArray.CreateBuilder<string>(inheritanceNamesLength);
for (var i = 0; i < inheritanceNamesLength; i++)
{
builder.Add(reader.ReadString());
}
return new DeclaredSymbolInfo(
name, immediateContainer, entireContainer, kind, new TextSpan(spanStart, spanLength),
builder.MoveToImmutable(),
parameterCount, typeParameterCount);
}
catch
{
return default(DeclaredSymbolInfo);
}
}
public async Task<ISymbol> ResolveAsync(Document document, CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
return Resolve(semanticModel, cancellationToken);
}
public ISymbol Resolve(SemanticModel semanticModel, CancellationToken cancellationToken)
{
var root = semanticModel.SyntaxTree.GetRoot(cancellationToken);
var node = root.FindNode(this.Span);
return semanticModel.GetDeclaredSymbol(node, cancellationToken);
}
}
}
......@@ -225,11 +225,11 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
var symbolToMatch = symbolInfoToMatch.Symbol;
var symbolToMatchCompilation = model.Compilation;
if (DependentTypeFinder.OriginalSymbolsMatch(searchSymbol, symbolInfoToMatch.Symbol, solution, null, symbolToMatchCompilation, cancellationToken))
if (SymbolFinder.OriginalSymbolsMatch(searchSymbol, symbolInfoToMatch.Symbol, solution, null, symbolToMatchCompilation, cancellationToken))
{
return ValueTuple.Create(true, CandidateReason.None);
}
else if (symbolInfoToMatch.CandidateSymbols.Any(s => DependentTypeFinder.OriginalSymbolsMatch(searchSymbol, s, solution, null, symbolToMatchCompilation, cancellationToken)))
else if (symbolInfoToMatch.CandidateSymbols.Any(s => SymbolFinder.OriginalSymbolsMatch(searchSymbol, s, solution, null, symbolToMatchCompilation, cancellationToken)))
{
return ValueTuple.Create(true, symbolInfoToMatch.CandidateReason);
}
......
......@@ -43,7 +43,7 @@ public static partial class SymbolFinder
if (member != null &&
member.IsOverride &&
member.OverriddenMember() != null &&
DependentTypeFinder.OriginalSymbolsMatch(member.OverriddenMember().OriginalDefinition, symbol.OriginalDefinition, solution, cancellationToken))
OriginalSymbolsMatch(member.OverriddenMember().OriginalDefinition, symbol.OriginalDefinition, solution, cancellationToken))
{
results = results ?? new List<ISymbol>();
results.Add(member);
......@@ -152,7 +152,7 @@ public static partial class SymbolFinder
throw new ArgumentNullException(nameof(solution));
}
return DependentTypeFinder.FindDerivedClassesAsync(type, solution, projects, cancellationToken);
return DependentTypeFinder.FindTransitivelyDerivedClassesAsync(type, solution, projects, cancellationToken);
}
/// <summary>
......@@ -166,13 +166,13 @@ public static partial class SymbolFinder
if (symbol is INamedTypeSymbol)
{
var namedTypeSymbol = (INamedTypeSymbol)symbol;
var implementingTypes = await DependentTypeFinder.FindImplementingTypesAsync(namedTypeSymbol, solution, projects, cancellationToken).ConfigureAwait(false);
var implementingTypes = await DependentTypeFinder.FindTransitivelyImplementingTypesAsync(namedTypeSymbol, solution, projects, cancellationToken).ConfigureAwait(false);
return implementingTypes.Where(IsAccessible);
}
else if (symbol.IsImplementableMember())
{
var containingType = symbol.ContainingType.OriginalDefinition;
var allTypes = await DependentTypeFinder.FindImplementingTypesAsync(containingType, solution, projects, cancellationToken).ConfigureAwait(false);
var allTypes = await DependentTypeFinder.FindTransitivelyImplementingTypesAsync(containingType, solution, projects, cancellationToken).ConfigureAwait(false);
List<ISymbol> results = null;
foreach (var t in allTypes)
......@@ -275,5 +275,257 @@ public static async Task<IEnumerable<SymbolCallerInfo>> FindCallersAsync(ISymbol
return SpecializedTasks.EmptyEnumerable<ReferencedSymbol>();
}
private static bool OriginalSymbolsMatch(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
CancellationToken cancellationToken)
{
if (ReferenceEquals(searchSymbol, symbolToMatch))
{
return true;
}
if (searchSymbol == null || symbolToMatch == null)
{
return false;
}
Compilation symbolToMatchCompilation = null;
if (!TryGetCompilation(symbolToMatch, solution, out symbolToMatchCompilation, cancellationToken))
{
return false;
}
return OriginalSymbolsMatch(searchSymbol, symbolToMatch, solution, null, symbolToMatchCompilation, cancellationToken);
}
internal static bool OriginalSymbolsMatch(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
if (symbolToMatch == null)
{
return false;
}
if (OriginalSymbolsMatchCore(searchSymbol, symbolToMatch, solution, searchSymbolCompilation, symbolToMatchCompilation, cancellationToken))
{
return true;
}
if (searchSymbol.Kind == SymbolKind.Namespace && symbolToMatch.Kind == SymbolKind.Namespace)
{
// if one of them is a merged namespace symbol and other one is its constituent namespace symbol, they are equivalent.
var namespace1 = (INamespaceSymbol)searchSymbol;
var namespace2 = (INamespaceSymbol)symbolToMatch;
var namespace1Count = namespace1.ConstituentNamespaces.Length;
var namespace2Count = namespace2.ConstituentNamespaces.Length;
if (namespace1Count != namespace2Count)
{
if ((namespace1Count > 1 &&
namespace1.ConstituentNamespaces.Any(n => NamespaceSymbolsMatch(n, namespace2, solution, cancellationToken))) ||
(namespace2Count > 1 &&
namespace2.ConstituentNamespaces.Any(n2 => NamespaceSymbolsMatch(namespace1, n2, solution, cancellationToken))))
{
return true;
}
}
}
if (searchSymbol.Kind == SymbolKind.NamedType && symbolToMatch.IsConstructor())
{
return OriginalSymbolsMatch(searchSymbol, symbolToMatch.ContainingType, solution, searchSymbolCompilation, symbolToMatchCompilation, cancellationToken);
}
return false;
}
private static bool OriginalSymbolsMatchCore(
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
if (searchSymbol == null || symbolToMatch == null)
{
return false;
}
searchSymbol = searchSymbol.GetOriginalUnreducedDefinition();
symbolToMatch = symbolToMatch.GetOriginalUnreducedDefinition();
// We compare the given searchSymbol and symbolToMatch for equivalence using SymbolEquivalenceComparer
// as follows:
// 1) We compare the given symbols using the SymbolEquivalenceComparer.IgnoreAssembliesInstance,
// which ignores the containing assemblies for named types equivalence checks. This is required
// to handle equivalent named types which are forwarded to completely different assemblies.
// 2) If the symbols are NOT equivalent ignoring assemblies, then they cannot be equivalent.
// 3) Otherwise, if the symbols ARE equivalent ignoring assemblies, they may or may not be equivalent
// if containing assemblies are NOT ignored. We need to perform additional checks to ensure they
// are indeed equivalent:
//
// (a) If IgnoreAssembliesInstance.Equals equivalence visitor encountered any pair of non-nested
// named types which were equivalent in all aspects, except that they resided in different
// assemblies, we need to ensure that all such pairs are indeed equivalent types. Such a pair
// of named types is equivalent if and only if one of them is a type defined in either
// searchSymbolCompilation(C1) or symbolToMatchCompilation(C2), say defined in reference assembly
// A (version v1) in compilation C1, and the other type is a forwarded type, such that it is
// forwarded from reference assembly A (version v2) to assembly B in compilation C2.
// (b) Otherwise, if no such named type pairs were encountered, symbols ARE equivalent.
using (var equivalentTypesWithDifferingAssemblies = SharedPools.Default<Dictionary<INamedTypeSymbol, INamedTypeSymbol>>().GetPooledObject())
{
// 1) Compare searchSymbol and symbolToMatch using SymbolEquivalenceComparer.IgnoreAssembliesInstance
if (!SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(searchSymbol, symbolToMatch, equivalentTypesWithDifferingAssemblies.Object))
{
// 2) If the symbols are NOT equivalent ignoring assemblies, then they cannot be equivalent.
return false;
}
// 3) If the symbols ARE equivalent ignoring assemblies, they may or may not be equivalent if containing assemblies are NOT ignored.
if (equivalentTypesWithDifferingAssemblies.Object.Count > 0)
{
// Step 3a) Ensure that all pairs of named types in equivalentTypesWithDifferingAssemblies are indeed equivalent types.
return VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies.Object, searchSymbol, symbolToMatch,
solution, searchSymbolCompilation, symbolToMatchCompilation, cancellationToken);
}
// 3b) If no such named type pairs were encountered, symbols ARE equivalent.
return true;
}
}
private static bool NamespaceSymbolsMatch(
INamespaceSymbol namespace1,
INamespaceSymbol namespace2,
Solution solution,
CancellationToken cancellationToken)
{
return OriginalSymbolsMatch(namespace1, namespace2, solution, cancellationToken);
}
// Verifies that all pairs of named types in equivalentTypesWithDifferingAssemblies are equivalent forwarded types.
private static bool VerifyForwardedTypes(
Dictionary<INamedTypeSymbol, INamedTypeSymbol> equivalentTypesWithDifferingAssemblies,
ISymbol searchSymbol,
ISymbol symbolToMatch,
Solution solution,
Compilation searchSymbolCompilation,
Compilation symbolToMatchCompilation,
CancellationToken cancellationToken)
{
var verifiedKeys = new HashSet<INamedTypeSymbol>();
var count = equivalentTypesWithDifferingAssemblies.Count;
int verifiedCount = 0;
// First check forwarded types in searchSymbolCompilation.
if (searchSymbolCompilation != null || TryGetCompilation(searchSymbol, solution, out searchSymbolCompilation, cancellationToken))
{
verifiedCount = VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, searchSymbolCompilation, verifiedKeys, isSearchSymbolCompilation: true);
if (verifiedCount == count)
{
// All equivalent types verified.
return true;
}
}
// Now check forwarded types in symbolToMatchCompilation.
verifiedCount += VerifyForwardedTypes(equivalentTypesWithDifferingAssemblies, symbolToMatchCompilation, verifiedKeys, isSearchSymbolCompilation: false);
return verifiedCount == count;
}
private static int VerifyForwardedTypes(
Dictionary<INamedTypeSymbol, INamedTypeSymbol> equivalentTypesWithDifferingAssemblies,
Compilation compilation,
HashSet<INamedTypeSymbol> verifiedKeys,
bool isSearchSymbolCompilation)
{
Contract.ThrowIfNull(compilation);
Contract.ThrowIfNull(equivalentTypesWithDifferingAssemblies);
Contract.ThrowIfTrue(!equivalentTypesWithDifferingAssemblies.Any());
// Must contain equivalents named types residing in different assemblies.
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => !SymbolEquivalenceComparer.Instance.Equals(kvp.Key.ContainingAssembly, kvp.Value.ContainingAssembly)));
// Must contain non-nested named types.
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => kvp.Key.ContainingType == null));
Contract.ThrowIfFalse(equivalentTypesWithDifferingAssemblies.All(kvp => kvp.Value.ContainingType == null));
var referencedAssemblies = new MultiDictionary<string, IAssemblySymbol>();
foreach (var assembly in compilation.GetReferencedAssemblySymbols())
{
referencedAssemblies.Add(assembly.Name, assembly);
}
int verifiedCount = 0;
foreach (var kvp in equivalentTypesWithDifferingAssemblies)
{
if (!verifiedKeys.Contains(kvp.Key))
{
INamedTypeSymbol originalType, expectedForwardedType;
if (isSearchSymbolCompilation)
{
originalType = kvp.Value.OriginalDefinition;
expectedForwardedType = kvp.Key.OriginalDefinition;
}
else
{
originalType = kvp.Key.OriginalDefinition;
expectedForwardedType = kvp.Value.OriginalDefinition;
}
foreach (var referencedAssembly in referencedAssemblies[originalType.ContainingAssembly.Name])
{
var fullyQualifiedTypeName = originalType.MetadataName;
if (originalType.ContainingNamespace != null)
{
fullyQualifiedTypeName = originalType.ContainingNamespace.ToDisplayString(SymbolDisplayFormats.SignatureFormat) +
"." + fullyQualifiedTypeName;
}
// Resolve forwarded type and verify that the types from different assembly are indeed equivalent.
var forwardedType = referencedAssembly.ResolveForwardedType(fullyQualifiedTypeName);
if (forwardedType == expectedForwardedType)
{
verifiedKeys.Add(kvp.Key);
verifiedCount++;
}
}
}
}
return verifiedCount;
}
private static bool TryGetCompilation(
ISymbol symbol,
Solution solution,
out Compilation definingCompilation,
CancellationToken cancellationToken)
{
var definitionProject = solution.GetProject(symbol.ContainingAssembly, cancellationToken);
if (definitionProject == null)
{
definingCompilation = null;
return false;
}
// compilation from definition project must already exist.
if (!definitionProject.TryGetCompilation(out definingCompilation))
{
Contract.Requires(false, "How can compilation not exist?");
return false;
}
return true;
}
}
}
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.FindSymbols
{
/// <summary>
/// Information about all the declarations defined within a document. Each declaration in the
/// document get a single item in <see cref="IDeclarationInfo.DeclaredSymbolInfos"/>.
/// </summary>
internal interface IDeclarationInfo
{
IReadOnlyList<DeclaredSymbolInfo> DeclaredSymbolInfos { get; }
}
}
\ No newline at end of file
......@@ -10,10 +10,10 @@
namespace Microsoft.CodeAnalysis.FindSymbols
{
internal class SyntaxTreeDeclarationInfo : AbstractSyntaxTreeInfo
internal class SyntaxTreeDeclarationInfo : AbstractSyntaxTreeInfo, IDeclarationInfo
{
private const string PersistenceName = "<SyntaxTreeInfoDeclarationPersistence>";
private const string SerializationFormat = "2";
private const string SerializationFormat = "3";
/// <summary>
/// in memory cache will hold onto any info related to opened documents in primary branch or all documents in forked branch
......@@ -23,9 +23,9 @@ internal class SyntaxTreeDeclarationInfo : AbstractSyntaxTreeInfo
private static readonly ConditionalWeakTable<BranchId, ConditionalWeakTable<DocumentId, AbstractSyntaxTreeInfo>> s_cache =
new ConditionalWeakTable<BranchId, ConditionalWeakTable<DocumentId, AbstractSyntaxTreeInfo>>();
public IEnumerable<DeclaredSymbolInfo> DeclaredSymbolInfos { get; }
public IReadOnlyList<DeclaredSymbolInfo> DeclaredSymbolInfos { get; }
public SyntaxTreeDeclarationInfo(VersionStamp version, IEnumerable<DeclaredSymbolInfo> declaredSymbolInfos)
public SyntaxTreeDeclarationInfo(VersionStamp version, IReadOnlyList<DeclaredSymbolInfo> declaredSymbolInfos)
: base(version)
{
DeclaredSymbolInfos = declaredSymbolInfos;
......@@ -33,7 +33,7 @@ public SyntaxTreeDeclarationInfo(VersionStamp version, IEnumerable<DeclaredSymbo
public override void WriteTo(ObjectWriter writer)
{
writer.WriteInt32(DeclaredSymbolInfos.Count());
writer.WriteInt32(DeclaredSymbolInfos.Count);
foreach (var declaredSymbolInfo in DeclaredSymbolInfos)
{
declaredSymbolInfo.WriteTo(writer);
......
......@@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols
internal partial class SyntaxTreeIdentifierInfo : AbstractSyntaxTreeInfo
{
private const string PersistenceName = "<SyntaxTreeInfoIdentifierPersistence>";
private const string SerializationFormat = "1";
private const string SerializationFormat = "2";
/// <summary>
/// in memory cache will hold onto any info related to opened documents in primary branch or all documents in forked branch
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.LanguageServices
{
internal abstract class AbstractSyntaxFactsService
{
private readonly static ObjectPool<List<Dictionary<string, string>>> s_aliasMapListPool =
new ObjectPool<List<Dictionary<string, string>>>(() => new List<Dictionary<string, string>>());
// Note: these names are stored case insensitively. That way the alias mapping works
// properly for VB. It will mean that our inheritance maps may store more links in them
// for C#. However, that's ok. It will be rare in practice, and all it means is that
// we'll end up examining slightly more types (likely 0) when doing operations like
// Find all references.
private readonly static ObjectPool<Dictionary<string, string>> s_aliasMapPool =
new ObjectPool<Dictionary<string, string>>(() => new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase));
protected static List<Dictionary<string, string>> AllocateAliasMapList()
{
return s_aliasMapListPool.Allocate();
}
protected static void FreeAliasMapList(List<Dictionary<string, string>> list)
{
if (list != null)
{
foreach (var aliasMap in list)
{
FreeAliasMap(aliasMap);
}
s_aliasMapListPool.ClearAndFree(list);
}
}
protected static void FreeAliasMap(Dictionary<string, string> aliasMap)
{
if (aliasMap != null)
{
s_aliasMapPool.ClearAndFree(aliasMap);
}
}
protected static Dictionary<string, string> AllocateAliasMap()
{
return s_aliasMapPool.Allocate();
}
}
}
......@@ -158,10 +158,9 @@ public static async Task<bool> IsForkedDocumentWithSyntaxChangesAsync(this Docum
}
}
public static async Task<IEnumerable<DeclaredSymbolInfo>> GetDeclaredSymbolInfosAsync(this Document document, CancellationToken cancellationToken)
public static async Task<IDeclarationInfo> GetDeclarationInfoAsync(this Document document, CancellationToken cancellationToken)
{
var declarationInfo = await SyntaxTreeInfo.GetDeclarationInfoAsync(document, cancellationToken).ConfigureAwait(false);
return declarationInfo.DeclaredSymbolInfos;
return await SyntaxTreeInfo.GetDeclarationInfoAsync(document, cancellationToken).ConfigureAwait(false);
}
/// <summary>
......
......@@ -101,10 +101,10 @@ public override ITypeSymbol VisitTypeParameter(ITypeParameterSymbol symbol)
var derivedImplementedTypesOfEachConstraintType = symbol.ConstraintTypes.Select(ct =>
{
var derivedAndImplementedTypes = new List<INamedTypeSymbol>();
return SymbolFinder.FindDerivedClassesAsync((INamedTypeSymbol)ct, _solution, immutableProjects, _cancellationToken).WaitAndGetResult(_cancellationToken)
.Concat(DependentTypeFinder.FindImplementingTypesAsync((INamedTypeSymbol)ct, _solution, immutableProjects, _cancellationToken).WaitAndGetResult(_cancellationToken))
.ToList();
});
var derivedClasses = SymbolFinder.FindDerivedClassesAsync((INamedTypeSymbol)ct, _solution, immutableProjects, _cancellationToken).WaitAndGetResult(_cancellationToken);
var implementedTypes = DependentTypeFinder.FindTransitivelyImplementingTypesAsync((INamedTypeSymbol)ct, _solution, immutableProjects, _cancellationToken).WaitAndGetResult(_cancellationToken);
return derivedClasses.Concat(implementedTypes).ToList();
}).ToList();
var intersectingTypes = derivedImplementedTypesOfEachConstraintType.Aggregate((x, y) => x.Intersect(y).ToList());
......
......@@ -71,6 +71,11 @@ public static bool IsDelegateType(this ITypeSymbol symbol)
return symbol?.TypeKind == TypeKind.Delegate;
}
public static bool IsStructType(this ITypeSymbol symbol)
{
return symbol?.TypeKind == TypeKind.Struct;
}
public static bool IsAnonymousType(this INamedTypeSymbol symbol)
{
return symbol?.IsAnonymousType == true;
......
// 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 Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Host
{
/// <summary>
......@@ -10,4 +8,4 @@ namespace Microsoft.CodeAnalysis.Host
public interface ILanguageService
{
}
}
}
\ No newline at end of file
......@@ -346,6 +346,8 @@
<Compile Include="Diagnostics\Extensions.cs" />
<Compile Include="Diagnostics\InternalDiagnosticsOptions.cs" />
<Compile Include="Diagnostics\InternalRuntimeDiagnosticOptions.cs" />
<Compile Include="FindSymbols\SyntaxTree\IDeclarationInfo.cs" />
<Compile Include="LanguageServices\SyntaxFactsService\AbstractSyntaxFactsService.cs" />
<Compile Include="SolutionCrawler\ExportIncrementalAnalyzerProviderAttribute.cs" />
<Compile Include="SolutionCrawler\IIncrementalAnalyzer.cs" />
<Compile Include="SolutionCrawler\IIncrementalAnalyzerProvider.cs" />
......
......@@ -45,7 +45,49 @@ public class DerivedClass : BaseClass { }
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedFromBase = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
[Fact]
public async Task ImmediatelyDerivedTypes_CSharp_AliasedNames()
{
var solution = new AdhocWorkspace().CurrentSolution;
// create portable assembly with an abstract base class
solution = AddProjectWithMetadataReferences(solution, "PortableProject", LanguageNames.CSharp, @"
namespace N
{
public abstract class BaseClass { }
}
", MscorlibRefPortable);
// create a normal assembly with a type derived from the portable abstract base
solution = AddProjectWithMetadataReferences(solution, "NormalProject", LanguageNames.CSharp, @"
using N;
using Alias1 = N.BaseClass;
namespace M
{
using Alias2 = Alias1;
public class DerivedClass : Alias2 { }
}
", MscorlibRef, solution.Projects.Single(pid => pid.Name == "PortableProject").Id);
// get symbols for types
var portableCompilation = await solution.Projects.Single(p => p.Name == "PortableProject").GetCompilationAsync();
var baseClassSymbol = portableCompilation.GetTypeByMetadataName("N.BaseClass");
var normalCompilation = await solution.Projects.Single(p => p.Name == "NormalProject").GetCompilationAsync();
var derivedClassSymbol = normalCompilation.GetTypeByMetadataName("M.DerivedClass");
// verify that the symbols are different (due to retargeting)
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
......@@ -84,7 +126,7 @@ public class DerivedClass : BaseClass { }
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedFromBase = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
......@@ -124,7 +166,7 @@ End Namespace
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedFromBase = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
......@@ -164,7 +206,7 @@ End Namespace
Assert.NotEqual(baseClassSymbol, derivedClassSymbol.BaseType);
// verify that the dependent types of `N.BaseClass` correctly resolve to `M.DerivedCLass`
var derivedFromBase = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedFromBase = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(baseClassSymbol, solution, CancellationToken.None);
var derivedDependentType = derivedFromBase.Single();
Assert.Equal(derivedClassSymbol, derivedDependentType);
}
......@@ -203,7 +245,8 @@ public class ImplementingClass : IBaseInterface { }
Assert.NotEqual(baseInterfaceSymbol, implementingClassSymbol.Interfaces.Single());
// verify that the implementing types of `N.IBaseInterface` correctly resolve to `M.ImplementingClass`
var typesThatImplementInterface = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(baseInterfaceSymbol, solution, CancellationToken.None);
var typesThatImplementInterface = await DependentTypeFinder.FindImmediatelyDerivedAndImplementingTypesAsync(
baseInterfaceSymbol, solution, CancellationToken.None);
Assert.Equal(implementingClassSymbol, typesThatImplementInterface.Single());
}
......@@ -242,7 +285,8 @@ End Namespace
Assert.NotEqual(baseInterfaceSymbol, implementingClassSymbol.Interfaces.Single());
// verify that the implementing types of `N.IBaseInterface` correctly resolve to `M.ImplementingClass`
var typesThatImplementInterface = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(baseInterfaceSymbol, solution, CancellationToken.None);
var typesThatImplementInterface = await DependentTypeFinder.FindImmediatelyDerivedAndImplementingTypesAsync(
baseInterfaceSymbol, solution, CancellationToken.None);
Assert.Equal(implementingClassSymbol, typesThatImplementInterface.Single());
}
......@@ -280,7 +324,8 @@ public class ImplementingClass : IBaseInterface { }
Assert.NotEqual(baseInterfaceSymbol, implementingClassSymbol.Interfaces.Single());
// verify that the implementing types of `N.IBaseInterface` correctly resolve to `M.ImplementingClass`
var typesThatImplementInterface = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(baseInterfaceSymbol, solution, CancellationToken.None);
var typesThatImplementInterface = await DependentTypeFinder.FindImmediatelyDerivedAndImplementingTypesAsync(
baseInterfaceSymbol, solution, CancellationToken.None);
Assert.Equal(implementingClassSymbol, typesThatImplementInterface.Single());
}
}
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Text
Imports System.Threading
......@@ -15,6 +16,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts
Namespace Microsoft.CodeAnalysis.VisualBasic
<ExportLanguageService(GetType(ISyntaxFactsService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicSyntaxFactsService
Inherits AbstractSyntaxFactsService
Implements ISyntaxFactsService
Public Function IsAwaitKeyword(token As SyntaxToken) As Boolean Implements ISyntaxFactsService.IsAwaitKeyword
......@@ -761,10 +763,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Select Case node.Kind()
Case SyntaxKind.ClassBlock
Dim classDecl = CType(node, ClassBlockSyntax)
declaredSymbolInfo = New DeclaredSymbolInfo(classDecl.ClassStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Class, classDecl.ClassStatement.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
classDecl.ClassStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Class, classDecl.ClassStatement.Identifier.Span,
GetInheritanceNames(classDecl))
Return True
Case SyntaxKind.ConstructorBlock
Dim constructor = CType(node, ConstructorBlockSyntax)
......@@ -776,38 +780,47 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Constructor,
constructor.SubNewStatement.NewKeyword.Span,
ImmutableArray(Of String).Empty,
parameterCount:=CType(If(constructor.SubNewStatement.ParameterList?.Parameters.Count, 0), UShort))
Return True
End If
Case SyntaxKind.DelegateFunctionStatement, SyntaxKind.DelegateSubStatement
Dim delegateDecl = CType(node, DelegateStatementSyntax)
declaredSymbolInfo = New DeclaredSymbolInfo(delegateDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Delegate, delegateDecl.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
delegateDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Delegate, delegateDecl.Identifier.Span,
ImmutableArray(Of String).Empty)
Return True
Case SyntaxKind.EnumBlock
Dim enumDecl = CType(node, EnumBlockSyntax)
declaredSymbolInfo = New DeclaredSymbolInfo(enumDecl.EnumStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Enum, enumDecl.EnumStatement.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
enumDecl.EnumStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Enum, enumDecl.EnumStatement.Identifier.Span,
ImmutableArray(Of String).Empty)
Return True
Case SyntaxKind.EnumMemberDeclaration
Dim enumMember = CType(node, EnumMemberDeclarationSyntax)
declaredSymbolInfo = New DeclaredSymbolInfo(enumMember.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.EnumMember, enumMember.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
enumMember.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.EnumMember, enumMember.Identifier.Span,
ImmutableArray(Of String).Empty)
Return True
Case SyntaxKind.EventStatement
Dim eventDecl = CType(node, EventStatementSyntax)
Dim eventParent = If(TypeOf node.Parent Is EventBlockSyntax, node.Parent.Parent, node.Parent)
declaredSymbolInfo = New DeclaredSymbolInfo(eventDecl.Identifier.ValueText,
GetContainerDisplayName(eventParent),
GetFullyQualifiedContainerName(eventParent),
DeclaredSymbolInfoKind.Event, eventDecl.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
eventDecl.Identifier.ValueText,
GetContainerDisplayName(eventParent),
GetFullyQualifiedContainerName(eventParent),
DeclaredSymbolInfoKind.Event, eventDecl.Identifier.Span,
ImmutableArray(Of String).Empty)
Return True
Case SyntaxKind.FunctionBlock, SyntaxKind.SubBlock
Dim funcDecl = CType(node, MethodBlockSyntax)
......@@ -817,15 +830,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Method,
funcDecl.SubOrFunctionStatement.Identifier.Span,
ImmutableArray(Of String).Empty,
parameterCount:=CType(If(funcDecl.SubOrFunctionStatement.ParameterList?.Parameters.Count, 0), UShort),
typeParameterCount:=CType(If(funcDecl.SubOrFunctionStatement.TypeParameterList?.Parameters.Count, 0), UShort))
Return True
Case SyntaxKind.InterfaceBlock
Dim interfaceDecl = CType(node, InterfaceBlockSyntax)
declaredSymbolInfo = New DeclaredSymbolInfo(interfaceDecl.InterfaceStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Interface, interfaceDecl.InterfaceStatement.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
interfaceDecl.InterfaceStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Interface, interfaceDecl.InterfaceStatement.Identifier.Span,
GetInheritanceNames(interfaceDecl))
Return True
Case SyntaxKind.ModifiedIdentifier
Dim modifiedIdentifier = CType(node, ModifiedIdentifierSyntax)
......@@ -835,33 +851,41 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim kind = If(fieldDecl.Modifiers.Any(Function(m) m.Kind() = SyntaxKind.ConstKeyword),
DeclaredSymbolInfoKind.Constant,
DeclaredSymbolInfoKind.Field)
declaredSymbolInfo = New DeclaredSymbolInfo(modifiedIdentifier.Identifier.ValueText,
GetContainerDisplayName(fieldDecl.Parent),
GetFullyQualifiedContainerName(fieldDecl.Parent),
kind, modifiedIdentifier.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
modifiedIdentifier.Identifier.ValueText,
GetContainerDisplayName(fieldDecl.Parent),
GetFullyQualifiedContainerName(fieldDecl.Parent),
kind, modifiedIdentifier.Identifier.Span,
ImmutableArray(Of String).Empty)
Return True
End If
Case SyntaxKind.ModuleBlock
Dim moduleDecl = CType(node, ModuleBlockSyntax)
declaredSymbolInfo = New DeclaredSymbolInfo(moduleDecl.ModuleStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Module, moduleDecl.ModuleStatement.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
moduleDecl.ModuleStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Module, moduleDecl.ModuleStatement.Identifier.Span,
GetInheritanceNames(moduleDecl))
Return True
Case SyntaxKind.PropertyStatement
Dim propertyDecl = CType(node, PropertyStatementSyntax)
Dim propertyParent = If(TypeOf node.Parent Is PropertyBlockSyntax, node.Parent.Parent, node.Parent)
declaredSymbolInfo = New DeclaredSymbolInfo(propertyDecl.Identifier.ValueText,
GetContainerDisplayName(propertyParent),
GetFullyQualifiedContainerName(propertyParent),
DeclaredSymbolInfoKind.Property, propertyDecl.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
propertyDecl.Identifier.ValueText,
GetContainerDisplayName(propertyParent),
GetFullyQualifiedContainerName(propertyParent),
DeclaredSymbolInfoKind.Property, propertyDecl.Identifier.Span,
ImmutableArray(Of String).Empty)
Return True
Case SyntaxKind.StructureBlock
Dim structDecl = CType(node, StructureBlockSyntax)
declaredSymbolInfo = New DeclaredSymbolInfo(structDecl.StructureStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Struct, structDecl.StructureStatement.Identifier.Span)
declaredSymbolInfo = New DeclaredSymbolInfo(
structDecl.StructureStatement.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Struct, structDecl.StructureStatement.Identifier.Span,
GetInheritanceNames(structDecl))
Return True
End Select
......@@ -869,6 +893,87 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return False
End Function
Private Function GetInheritanceNames(typeBlock As TypeBlockSyntax) As ImmutableArray(Of String)
Dim builder = ImmutableArray.CreateBuilder(Of String)
Dim aliasMap = GetAliasMap(typeBlock)
Try
For Each inheritsStatement In typeBlock.Inherits
AddInheritanceNames(builder, inheritsStatement.Types, aliasMap)
Next
For Each implementsStatement In typeBlock.Implements
AddInheritanceNames(builder, implementsStatement.Types, aliasMap)
Next
Return builder.ToImmutable()
Finally
FreeAliasMap(aliasMap)
End Try
End Function
Private Function GetAliasMap(typeBlock As TypeBlockSyntax) As Dictionary(Of String, String)
Dim compilationUnit = typeBlock.SyntaxTree.GetCompilationUnitRoot()
Dim aliasMap As Dictionary(Of String, String) = Nothing
For Each import In compilationUnit.Imports
For Each clause In import.ImportsClauses
If clause.IsKind(SyntaxKind.SimpleImportsClause) Then
Dim simpleImport = DirectCast(clause, SimpleImportsClauseSyntax)
If simpleImport.Alias IsNot Nothing Then
Dim mappedName = GetTypeName(simpleImport.Name)
If mappedName IsNot Nothing Then
aliasMap = If(aliasMap, AllocateAliasMap())
aliasMap(simpleImport.Alias.Identifier.ValueText) = mappedName
End If
End If
End If
Next
Next
Return aliasMap
End Function
Private Sub AddInheritanceNames(
builder As ImmutableArray(Of String).Builder,
types As SeparatedSyntaxList(Of TypeSyntax),
aliasMap As Dictionary(Of String, String))
For Each typeSyntax In types
AddInheritanceName(builder, typeSyntax, aliasMap)
Next
End Sub
Private Sub AddInheritanceName(
builder As ImmutableArray(Of String).Builder,
typeSyntax As TypeSyntax,
aliasMap As Dictionary(Of String, String))
Dim name = GetTypeName(typeSyntax)
If name IsNot Nothing Then
builder.Add(name)
Dim mappedName As String = Nothing
If aliasMap?.TryGetValue(name, mappedName) = True Then
' Looks Like this could be an alias. Also include the name the alias points to
builder.Add(mappedName)
End If
End If
End Sub
Private Function GetTypeName(typeSyntax As TypeSyntax) As String
If TypeOf typeSyntax Is SimpleNameSyntax Then
Return GetSimpleName(DirectCast(typeSyntax, SimpleNameSyntax))
ElseIf TypeOf typeSyntax Is QualifiedNameSyntax Then
Return GetSimpleName(DirectCast(typeSyntax, QualifiedNameSyntax).Right)
End If
Return Nothing
End Function
Private Function GetSimpleName(simpleName As SimpleNameSyntax) As String
Return simpleName.Identifier.ValueText
End Function
Private Function GetContainerDisplayName(node As SyntaxNode) As String
Return GetDisplayName(node, DisplayNameOptions.IncludeTypeParameters)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册