Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
9623d557
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
9623d557
编写于
3月 01, 2016
作者:
B
Brett V. Forsgren
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #9056 from brettfo/type-equality
Use smarter type equality in `DependentTypeFinder`
上级
836d6a26
233d1233
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
562 addition
and
237 deletion
+562
-237
src/EditorFeatures/Core/EditorFeatures.csproj
src/EditorFeatures/Core/EditorFeatures.csproj
+1
-1
src/EditorFeatures/Core/Shared/IDocumentSupportsFeatureService.cs
...orFeatures/Core/Shared/IDocumentSupportsFeatureService.cs
+0
-0
src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs
...ortable/FindSymbols/FindReferences/DependentTypeFinder.cs
+254
-2
src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs
...Symbols/FindReferences/Finders/AbstractReferenceFinder.cs
+2
-234
src/Workspaces/CoreTest/DependentTypeFinderTests.cs
src/Workspaces/CoreTest/DependentTypeFinderTests.cs
+304
-0
src/Workspaces/CoreTest/ServicesTest.csproj
src/Workspaces/CoreTest/ServicesTest.csproj
+1
-0
未找到文件。
src/EditorFeatures/Core/EditorFeatures.csproj
浏览文件 @
9623d557
...
...
@@ -735,7 +735,7 @@
<Compile
Include=
"Shared\Preview\PreviewSolutionCrawlerRegistrationService.cs"
/>
<Compile
Include=
"Shared\Preview\PredefinedPreviewTaggerKeys.cs"
/>
<Compile
Include=
"Shared\Preview\PreviewWorkspace.cs"
/>
<Compile
Include=
"Shared\IDocumentSupports
Suggestion
Service.cs"
/>
<Compile
Include=
"Shared\IDocumentSupports
Feature
Service.cs"
/>
<Compile
Include=
"Shared\Tagging\EventSources\AbstractTaggerEventSource.cs"
/>
<Compile
Include=
"Shared\Tagging\EventSources\AbstractWorkspaceTrackingTaggerEventSource.cs"
/>
<Compile
Include=
"Shared\Tagging\EventSources\TaggerEventSources.WorkspaceRegistrationChangedEventSource.cs"
/>
...
...
src/EditorFeatures/Core/Shared/IDocumentSupports
Suggestion
Service.cs
→
src/EditorFeatures/Core/Shared/IDocumentSupports
Feature
Service.cs
浏览文件 @
9623d557
文件已移动
src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs
浏览文件 @
9623d557
...
...
@@ -159,7 +159,7 @@ internal static class DependentTypeFinder
type
,
solution
,
null
,
(
t1
,
t2
)
=>
t1
.
BaseType
==
t2
,
(
candidate
,
baseType
)
=>
OriginalSymbolsMatch
(
candidate
.
BaseType
,
baseType
,
solution
,
cancellationToken
)
,
s_derivedClassesCache
,
cancellationToken
);
}
...
...
@@ -179,7 +179,7 @@ internal static class DependentTypeFinder
type
,
solution
,
null
,
(
i1
,
i2
)
=>
i1
.
Interfaces
.
Contains
(
i2
),
(
candidate
,
baseInterface
)
=>
candidate
.
Interfaces
.
Any
(
i
=>
OriginalSymbolsMatch
(
i
,
baseInterface
,
solution
,
cancellationToken
)
),
s_derivedInterfacesCache
,
cancellationToken
);
}
...
...
@@ -341,5 +341,257 @@ private static List<SymbolKey> GetOrAddDependentTypes(ConditionalWeakTable<Compi
.
GetOrAdd
(
typeId
,
dependentTypeIds
);
}
}
internal
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
;
}
}
}
src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs
浏览文件 @
9623d557
...
...
@@ -217,8 +217,6 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
protected
static
Func
<
SyntaxNode
,
SemanticModel
,
ValueTuple
<
bool
,
CandidateReason
>>
GetStandardSymbolsNodeMatchFunction
(
ISymbol
searchSymbol
,
Solution
solution
,
CancellationToken
cancellationToken
)
{
Compilation
searchSymbolCompilation
=
null
;
Func
<
SyntaxNode
,
SemanticModel
,
ValueTuple
<
bool
,
CandidateReason
>>
symbolsMatch
=
(
node
,
model
)
=>
{
...
...
@@ -227,11 +225,11 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
var
symbolToMatch
=
symbolInfoToMatch
.
Symbol
;
var
symbolToMatchCompilation
=
model
.
Compilation
;
if
(
OriginalSymbolsMatch
(
searchSymbol
,
symbolInfoToMatch
.
Symbol
,
solution
,
ref
searchSymbolCompilation
,
symbolToMatchCompilation
,
cancellationToken
))
if
(
DependentTypeFinder
.
OriginalSymbolsMatch
(
searchSymbol
,
symbolInfoToMatch
.
Symbol
,
solution
,
null
,
symbolToMatchCompilation
,
cancellationToken
))
{
return
ValueTuple
.
Create
(
true
,
CandidateReason
.
None
);
}
else
if
(
symbolInfoToMatch
.
CandidateSymbols
.
Any
(
s
=>
OriginalSymbolsMatch
(
searchSymbol
,
s
,
solution
,
ref
searchSymbolCompilation
,
symbolToMatchCompilation
,
cancellationToken
)))
else
if
(
symbolInfoToMatch
.
CandidateSymbols
.
Any
(
s
=>
DependentTypeFinder
.
OriginalSymbolsMatch
(
searchSymbol
,
s
,
solution
,
null
,
symbolToMatchCompilation
,
cancellationToken
)))
{
return
ValueTuple
.
Create
(
true
,
symbolInfoToMatch
.
CandidateReason
);
}
...
...
@@ -244,236 +242,6 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
return
symbolsMatch
;
}
private
static
bool
OriginalSymbolsMatch
(
ISymbol
searchSymbol
,
ISymbol
symbolToMatch
,
Solution
solution
,
ref
Compilation
searchSymbolCompilation
,
Compilation
symbolToMatchCompilation
,
CancellationToken
cancellationToken
)
{
if
(
symbolToMatch
==
null
)
{
return
false
;
}
if
(
OriginalSymbolsMatchCore
(
searchSymbol
,
symbolToMatch
,
solution
,
ref
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
,
ref
searchSymbolCompilation
,
symbolToMatchCompilation
,
cancellationToken
);
}
return
false
;
}
private
static
bool
NamespaceSymbolsMatch
(
INamespaceSymbol
namespace1
,
INamespaceSymbol
namespace2
,
Solution
solution
,
CancellationToken
cancellationToken
)
{
Compilation
definingCompilation
=
null
;
return
OriginalSymbolsMatch
(
namespace1
,
namespace2
,
solution
,
ref
definingCompilation
,
null
,
cancellationToken
);
}
private
static
bool
OriginalSymbolsMatchCore
(
ISymbol
searchSymbol
,
ISymbol
symbolToMatch
,
Solution
solution
,
ref
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
,
ref
searchSymbolCompilation
,
symbolToMatchCompilation
,
cancellationToken
);
}
// 3b) If no such named type pairs were encountered, symbols ARE equivalent.
return
true
;
}
}
// 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
,
ref
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
(
TryGetCompilation
(
searchSymbol
,
solution
,
ref
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
,
ref
Compilation
definingCompilation
,
CancellationToken
cancellationToken
)
{
if
(
definingCompilation
==
null
)
{
var
definitionProject
=
solution
.
GetProject
(
symbol
.
ContainingAssembly
,
cancellationToken
);
if
(
definitionProject
==
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
;
}
protected
Task
<
IEnumerable
<
ReferenceLocation
>>
FindReferencesInTokensAsync
(
TSymbol
symbol
,
Document
document
,
...
...
src/Workspaces/CoreTest/DependentTypeFinderTests.cs
0 → 100644
浏览文件 @
9623d557
// 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.Linq
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.FindSymbols
;
using
Microsoft.CodeAnalysis.Text
;
using
Roslyn.Test.Utilities
;
using
Xunit
;
namespace
Microsoft.CodeAnalysis.UnitTests
{
public
class
DependentTypeFinderTests
:
TestBase
{
private
Solution
AddProject
(
Solution
solution
,
string
projectName
,
string
languageName
,
string
code
,
MetadataReference
metadataReference
,
params
ProjectId
[]
projectReferences
)
{
var
suffix
=
languageName
==
LanguageNames
.
CSharp
?
"cs"
:
"vb"
;
var
pid
=
ProjectId
.
CreateNewId
();
var
did
=
DocumentId
.
CreateNewId
(
pid
);
var
pi
=
ProjectInfo
.
Create
(
pid
,
VersionStamp
.
Default
,
projectName
,
projectName
,
languageName
,
metadataReferences
:
new
[]
{
metadataReference
},
projectReferences
:
projectReferences
.
Select
(
p
=>
new
ProjectReference
(
p
)));
return
solution
.
AddProject
(
pi
).
AddDocument
(
did
,
$"
{
projectName
}
.
{
suffix
}
"
,
SourceText
.
From
(
code
));
}
[
WorkItem
(
4973
,
"https://github.com/dotnet/roslyn/issues/4973"
)]
[
Fact
]
public
async
Task
ImmediatelyDerivedTypes_CSharp
()
{
var
solution
=
new
AdhocWorkspace
().
CurrentSolution
;
// create portable assembly with an abstract base class
solution
=
AddProject
(
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
=
AddProject
(
solution
,
"NormalProject"
,
LanguageNames
.
CSharp
,
@"
using N;
namespace M
{
public class DerivedClass : BaseClass { }
}
"
,
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
.
GetTypesImmediatelyDerivedFromClassesAsync
(
baseClassSymbol
,
solution
,
CancellationToken
.
None
);
var
derivedDependentType
=
derivedFromBase
.
Single
();
Assert
.
Equal
(
derivedClassSymbol
,
derivedDependentType
);
}
[
WorkItem
(
4973
,
"https://github.com/dotnet/roslyn/issues/4973"
)]
[
Fact
]
public
async
Task
ImmediatelyDerivedTypes_CSharp_PortableProfile7
()
{
var
solution
=
new
AdhocWorkspace
().
CurrentSolution
;
// create portable assembly with an abstract base class
solution
=
AddProject
(
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
=
AddProject
(
solution
,
"NormalProject"
,
LanguageNames
.
CSharp
,
@"
using N;
namespace M
{
public class DerivedClass : BaseClass { }
}
"
,
SystemRuntimePP7Ref
,
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
.
GetTypesImmediatelyDerivedFromClassesAsync
(
baseClassSymbol
,
solution
,
CancellationToken
.
None
);
var
derivedDependentType
=
derivedFromBase
.
Single
();
Assert
.
Equal
(
derivedClassSymbol
,
derivedDependentType
);
}
[
WorkItem
(
4973
,
"https://github.com/dotnet/roslyn/issues/4973"
)]
[
Fact
]
public
async
Task
ImmediatelyDerivedTypes_VisualBasic
()
{
var
solution
=
new
AdhocWorkspace
().
CurrentSolution
;
// create portable assembly with an abstract base class
solution
=
AddProject
(
solution
,
"PortableProject"
,
LanguageNames
.
VisualBasic
,
@"
Namespace N
Public MustInherit Class BaseClass
End Class
End Namespace
"
,
MscorlibRefPortable
);
// create a normal assembly with a type derived from the portable abstract base
solution
=
AddProject
(
solution
,
"NormalProject"
,
LanguageNames
.
VisualBasic
,
@"
Imports N
Namespace M
Public Class DerivedClass
Inherits BaseClass
End Class
End Namespace
"
,
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
.
GetTypesImmediatelyDerivedFromClassesAsync
(
baseClassSymbol
,
solution
,
CancellationToken
.
None
);
var
derivedDependentType
=
derivedFromBase
.
Single
();
Assert
.
Equal
(
derivedClassSymbol
,
derivedDependentType
);
}
[
WorkItem
(
4973
,
"https://github.com/dotnet/roslyn/issues/4973"
)]
[
Fact
]
public
async
Task
ImmediatelyDerivedTypes_CrossLanguage
()
{
var
solution
=
new
AdhocWorkspace
().
CurrentSolution
;
// create portable assembly with an abstract base class
solution
=
AddProject
(
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
=
AddProject
(
solution
,
"NormalProject"
,
LanguageNames
.
VisualBasic
,
@"
Imports N
Namespace M
Public Class DerivedClass
Inherits BaseClass
End Class
End Namespace
"
,
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
.
GetTypesImmediatelyDerivedFromClassesAsync
(
baseClassSymbol
,
solution
,
CancellationToken
.
None
);
var
derivedDependentType
=
derivedFromBase
.
Single
();
Assert
.
Equal
(
derivedClassSymbol
,
derivedDependentType
);
}
[
WorkItem
(
4973
,
"https://github.com/dotnet/roslyn/issues/4973"
)]
[
Fact
]
public
async
Task
ImmediatelyDerivedInterfaces_CSharp
()
{
var
solution
=
new
AdhocWorkspace
().
CurrentSolution
;
// create portable assembly with an interface
solution
=
AddProject
(
solution
,
"PortableProject"
,
LanguageNames
.
CSharp
,
@"
namespace N
{
public interface IBaseInterface { }
}
"
,
MscorlibRefPortable
);
// create a normal assembly with a type implementing that interface
solution
=
AddProject
(
solution
,
"NormalProject"
,
LanguageNames
.
CSharp
,
@"
using N;
namespace M
{
public class ImplementingClass : IBaseInterface { }
}
"
,
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
baseInterfaceSymbol
=
portableCompilation
.
GetTypeByMetadataName
(
"N.IBaseInterface"
);
var
normalCompilation
=
await
solution
.
Projects
.
Single
(
p
=>
p
.
Name
==
"NormalProject"
).
GetCompilationAsync
();
var
implementingClassSymbol
=
normalCompilation
.
GetTypeByMetadataName
(
"M.ImplementingClass"
);
// verify that the symbols are different (due to retargeting)
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
);
Assert
.
Equal
(
implementingClassSymbol
,
typesThatImplementInterface
.
Single
());
}
[
WorkItem
(
4973
,
"https://github.com/dotnet/roslyn/issues/4973"
)]
[
Fact
]
public
async
Task
ImmediatelyDerivedInterfaces_VisualBasic
()
{
var
solution
=
new
AdhocWorkspace
().
CurrentSolution
;
// create portable assembly with an interface
solution
=
AddProject
(
solution
,
"PortableProject"
,
LanguageNames
.
VisualBasic
,
@"
Namespace N
Public Interface IBaseInterface
End Interface
End Namespace
"
,
MscorlibRefPortable
);
// create a normal assembly with a type implementing that interface
solution
=
AddProject
(
solution
,
"NormalProject"
,
LanguageNames
.
VisualBasic
,
@"
Imports N
Namespace M
Public Class ImplementingClass
Implements IBaseInterface
End Class
End Namespace
"
,
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
baseInterfaceSymbol
=
portableCompilation
.
GetTypeByMetadataName
(
"N.IBaseInterface"
);
var
normalCompilation
=
await
solution
.
Projects
.
Single
(
p
=>
p
.
Name
==
"NormalProject"
).
GetCompilationAsync
();
var
implementingClassSymbol
=
normalCompilation
.
GetTypeByMetadataName
(
"M.ImplementingClass"
);
// verify that the symbols are different (due to retargeting)
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
);
Assert
.
Equal
(
implementingClassSymbol
,
typesThatImplementInterface
.
Single
());
}
[
WorkItem
(
4973
,
"https://github.com/dotnet/roslyn/issues/4973"
)]
[
Fact
]
public
async
Task
ImmediatelyDerivedInterfaces_CrossLanguage
()
{
var
solution
=
new
AdhocWorkspace
().
CurrentSolution
;
// create portable assembly with an interface
solution
=
AddProject
(
solution
,
"PortableProject"
,
LanguageNames
.
VisualBasic
,
@"
Namespace N
Public Interface IBaseInterface
End Interface
End Namespace
"
,
MscorlibRefPortable
);
// create a normal assembly with a type implementing that interface
solution
=
AddProject
(
solution
,
"NormalProject"
,
LanguageNames
.
CSharp
,
@"
using N;
namespace M
{
public class ImplementingClass : IBaseInterface { }
}
"
,
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
baseInterfaceSymbol
=
portableCompilation
.
GetTypeByMetadataName
(
"N.IBaseInterface"
);
var
normalCompilation
=
await
solution
.
Projects
.
Single
(
p
=>
p
.
Name
==
"NormalProject"
).
GetCompilationAsync
();
var
implementingClassSymbol
=
normalCompilation
.
GetTypeByMetadataName
(
"M.ImplementingClass"
);
// verify that the symbols are different (due to retargeting)
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
);
Assert
.
Equal
(
implementingClassSymbol
,
typesThatImplementInterface
.
Single
());
}
}
}
src/Workspaces/CoreTest/ServicesTest.csproj
浏览文件 @
9623d557
...
...
@@ -66,6 +66,7 @@
<CodeAnalysisRuleSet>
..\..\..\build\TestProjectRules.ruleset
</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile
Include=
"DependentTypeFinderTests.cs"
/>
<Compile
Include=
"Editting\SyntaxEditorTests.cs"
/>
<Compile
Include=
"ExtensionOrdererTests.cs"
/>
<Compile
Include=
"Host\WorkspaceServices\TestProjectCacheService.cs"
/>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录