Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
80e9227e
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,体验更适合开发者的 AI 搜索 >>
未验证
提交
80e9227e
编写于
12月 19, 2020
作者:
M
msftbot[bot]
提交者:
GitHub
12月 19, 2020
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #50073 from dotnet/merges/release/dev16.9-to-master
Merge release/dev16.9 to master
上级
24197adb
cf42e6bd
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
352 addition
and
28 deletion
+352
-28
src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
+4
-0
src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerConfigTests.cs
...rs/Core/CodeAnalysisTest/Analyzers/AnalyzerConfigTests.cs
+252
-0
src/Compilers/Core/Portable/CommandLine/AnalyzerConfig.cs
src/Compilers/Core/Portable/CommandLine/AnalyzerConfig.cs
+50
-1
src/Compilers/Core/Portable/CommandLine/AnalyzerConfigSet.cs
src/Compilers/Core/Portable/CommandLine/AnalyzerConfigSet.cs
+46
-27
未找到文件。
src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
浏览文件 @
80e9227e
...
...
@@ -13016,11 +13016,13 @@ class C
var analyzerConfigFile = dir.CreateFile(".globalconfig");
var analyzerConfig = analyzerConfigFile.WriteAllText(@"
is_global = true
global_level = 100
option1 = abc");
var analyzerConfigFile2 = dir.CreateFile(".globalconfig2");
var analyzerConfig2 = analyzerConfigFile2.WriteAllText(@"
is_global = true
global_level = 100
option1 = def");
var output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzerconfig:" + analyzerConfig.Path + "," + analyzerConfig2.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false);
...
...
@@ -13033,11 +13035,13 @@ class C
analyzerConfig = analyzerConfigFile.WriteAllText(@"
is_global = true
global_level = 100
[/file.cs]
option1 = abc");
analyzerConfig2 = analyzerConfigFile2.WriteAllText(@"
is_global = true
global_level = 100
[/file.cs]
option1 = def");
...
...
src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerConfigTests.cs
浏览文件 @
80e9227e
...
...
@@ -2175,6 +2175,258 @@ public void GlobalConfigIssuesWarningWithInvalidSectionNames(string sectionName,
public
void
GlobalConfigIssuesWarningWithInvalidSectionNames_PlatformSpecific
(
string
sectionName
,
bool
isValidWindows
,
bool
isValidOther
)
=>
GlobalConfigIssuesWarningWithInvalidSectionNames
(
sectionName
,
ExecutionConditionUtil
.
IsWindows
?
isValidWindows
:
isValidOther
);
[
Theory
]
[
InlineData
(
"/.globalconfig"
,
true
)]
[
InlineData
(
"/.GLOBALCONFIG"
,
true
)]
[
InlineData
(
"/.glObalConfiG"
,
true
)]
[
InlineData
(
"/path/to/.globalconfig"
,
true
)]
[
InlineData
(
"/my.globalconfig"
,
false
)]
[
InlineData
(
"/globalconfig"
,
false
)]
[
InlineData
(
"/path/to/globalconfig"
,
false
)]
[
InlineData
(
"/path/to/my.globalconfig"
,
false
)]
[
InlineData
(
"/.editorconfig"
,
false
)]
[
InlineData
(
"/.globalconfİg"
,
false
)]
public
void
FileNameCausesConfigToBeReportedAsGlobal
(
string
fileName
,
bool
shouldBeTreatedAsGlobal
)
{
var
config
=
Parse
(
""
,
fileName
);
Assert
.
Equal
(
shouldBeTreatedAsGlobal
,
config
.
IsGlobal
);
}
[
Fact
]
public
void
GlobalLevelCanBeReadFromAnyConfig
()
{
var
config
=
Parse
(
"global_level = 5"
,
"/.editorconfig"
);
Assert
.
Equal
(
5
,
config
.
GlobalLevel
);
}
[
Fact
]
public
void
GlobalLevelDefaultsTo100ForUserGlobalConfigs
()
{
var
config
=
Parse
(
""
,
"/"
+
AnalyzerConfig
.
UserGlobalConfigName
);
Assert
.
True
(
config
.
IsGlobal
);
Assert
.
Equal
(
100
,
config
.
GlobalLevel
);
}
[
Fact
]
public
void
GlobalLevelCanBeOverriddenForUserGlobalConfigs
()
{
var
config
=
Parse
(
"global_level = 5"
,
"/"
+
AnalyzerConfig
.
UserGlobalConfigName
);
Assert
.
True
(
config
.
IsGlobal
);
Assert
.
Equal
(
5
,
config
.
GlobalLevel
);
}
[
Fact
]
public
void
GlobalLevelDefaultsToZeroForNonUserGlobalConfigs
()
{
var
config
=
Parse
(
"is_global = true"
,
"/.nugetconfig"
);
Assert
.
True
(
config
.
IsGlobal
);
Assert
.
Equal
(
0
,
config
.
GlobalLevel
);
}
[
Fact
]
public
void
GlobalLevelIsNotPresentInConfigSet
()
{
var
config
=
Parse
(
"global_level = 123"
,
"/.globalconfig"
);
var
set
=
AnalyzerConfigSet
.
Create
(
ImmutableArray
.
Create
(
config
));
var
globalOptions
=
set
.
GlobalConfigOptions
;
Assert
.
Empty
(
globalOptions
.
AnalyzerOptions
);
Assert
.
Empty
(
globalOptions
.
TreeOptions
);
Assert
.
Empty
(
globalOptions
.
Diagnostics
);
}
[
Fact
]
public
void
GlobalLevelInSectionIsPresentInConfigSet
()
{
var
config
=
Parse
(
@"
[/path]
global_level = 123"
,
"/.globalconfig"
);
var
set
=
AnalyzerConfigSet
.
Create
(
ImmutableArray
.
Create
(
config
));
var
globalOptions
=
set
.
GlobalConfigOptions
;
Assert
.
Empty
(
globalOptions
.
AnalyzerOptions
);
Assert
.
Empty
(
globalOptions
.
TreeOptions
);
Assert
.
Empty
(
globalOptions
.
Diagnostics
);
var
sectionOptions
=
set
.
GetOptionsForSourcePath
(
"/path"
);
Assert
.
Single
(
sectionOptions
.
AnalyzerOptions
);
Assert
.
Equal
(
"123"
,
sectionOptions
.
AnalyzerOptions
[
"global_level"
]);
Assert
.
Empty
(
sectionOptions
.
TreeOptions
);
Assert
.
Empty
(
sectionOptions
.
Diagnostics
);
}
[
Theory
]
[
InlineData
(
1
,
2
)]
[
InlineData
(
2
,
1
)]
[
InlineData
(-
2
,
-
1
)]
[
InlineData
(
2
,
-
1
)]
public
void
GlobalLevelAllowsOverrideOfGlobalKeys
(
int
level1
,
int
level2
)
{
var
configs
=
ArrayBuilder
<
AnalyzerConfig
>.
GetInstance
();
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level =
{
level1
}
option1 = value1
"
,
"/.globalconfig1"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level =
{
level2
}
option1 = value2"
,
"/.globalconfig2"
));
var
globalConfig
=
AnalyzerConfigSet
.
MergeGlobalConfigs
(
configs
,
out
var
diagnostics
);
diagnostics
.
Verify
();
Assert
.
Single
(
globalConfig
.
GlobalSection
.
Properties
.
Keys
,
"option1"
);
string
expectedValue
=
level1
>
level2
?
"value1"
:
"value2"
;
Assert
.
Single
(
globalConfig
.
GlobalSection
.
Properties
.
Values
,
expectedValue
);
configs
.
Free
();
}
[
Fact
]
public
void
GlobalLevelAllowsOverrideOfSectionKeys
()
{
var
configs
=
ArrayBuilder
<
AnalyzerConfig
>.
GetInstance
();
configs
.
Add
(
Parse
(
@"
is_global = true
global_level = 1
[/path]
option1 = value1
"
,
"/.globalconfig1"
));
configs
.
Add
(
Parse
(
@"
is_global = true
global_level = 2
[/path]
option1 = value2"
,
"/.globalconfig2"
));
var
globalConfig
=
AnalyzerConfigSet
.
MergeGlobalConfigs
(
configs
,
out
var
diagnostics
);
diagnostics
.
Verify
();
Assert
.
Single
(
globalConfig
.
NamedSections
);
Assert
.
Equal
(
"/path"
,
globalConfig
.
NamedSections
[
0
].
Name
);
Assert
.
Single
(
globalConfig
.
NamedSections
[
0
].
Properties
.
Keys
,
"option1"
);
Assert
.
Single
(
globalConfig
.
NamedSections
[
0
].
Properties
.
Values
,
"value2"
);
configs
.
Free
();
}
[
Theory
]
[
InlineData
(
1
,
2
,
3
,
"value3"
)]
[
InlineData
(
2
,
1
,
3
,
"value3"
)]
[
InlineData
(
3
,
2
,
1
,
"value1"
)]
[
InlineData
(
1
,
2
,
1
,
"value2"
)]
[
InlineData
(
1
,
1
,
2
,
"value3"
)]
[
InlineData
(
2
,
1
,
1
,
"value1"
)]
public
void
GlobalLevelAllowsOverrideOfDuplicateGlobalKeys
(
int
level1
,
int
level2
,
int
level3
,
string
expectedValue
)
{
var
configs
=
ArrayBuilder
<
AnalyzerConfig
>.
GetInstance
();
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level =
{
level1
}
option1 = value1
"
,
"/.globalconfig1"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level =
{
level2
}
option1 = value2"
,
"/.globalconfig2"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level =
{
level3
}
option1 = value3"
,
"/.globalconfig3"
));
var
globalConfig
=
AnalyzerConfigSet
.
MergeGlobalConfigs
(
configs
,
out
var
diagnostics
);
diagnostics
.
Verify
();
Assert
.
Single
(
globalConfig
.
GlobalSection
.
Properties
.
Keys
,
"option1"
);
Assert
.
Single
(
globalConfig
.
GlobalSection
.
Properties
.
Values
,
expectedValue
);
configs
.
Free
();
}
[
Fact
]
public
void
GlobalLevelReportsConflictsOnlyAtTheHighestLevel
()
{
var
configs
=
ArrayBuilder
<
AnalyzerConfig
>.
GetInstance
();
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level = 1
option1 = value1
"
,
"/.globalconfig1"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level = 1
option1 = value2"
,
"/.globalconfig2"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level = 3
option1 = value3"
,
"/.globalconfig3"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level = 3
option1 = value4"
,
"/.globalconfig4"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level = 2
option1 = value5"
,
"/.globalconfig5"
));
configs
.
Add
(
Parse
(
$@"
is_global = true
global_level = 2
option1 = value6"
,
"/.globalconfig6"
));
var
globalConfig
=
AnalyzerConfigSet
.
MergeGlobalConfigs
(
configs
,
out
var
diagnostics
);
// we don't report config1, 2, 5, or 6, because they didn't conflict: 3 + 4 overrode them, but then themselves were conflicting
diagnostics
.
Verify
(
Diagnostic
(
"MultipleGlobalAnalyzerKeys"
).
WithArguments
(
"option1"
,
"Global Section"
,
"/.globalconfig3, /.globalconfig4"
).
WithLocation
(
1
,
1
)
);
configs
.
Free
();
}
[
Fact
]
public
void
InvalidGlobalLevelIsIgnored
()
{
var
userGlobalConfig
=
Parse
(
$@"
is_global = true
global_level = abc
"
,
"/.globalconfig"
);
var
nonUserGlobalConfig
=
Parse
(
$@"
is_global = true
global_level = abc
"
,
"/.editorconfig"
);
Assert
.
Equal
(
100
,
userGlobalConfig
.
GlobalLevel
);
Assert
.
Equal
(
0
,
nonUserGlobalConfig
.
GlobalLevel
);
}
#
endregion
}
}
src/Compilers/Core/Portable/CommandLine/AnalyzerConfig.cs
浏览文件 @
80e9227e
...
...
@@ -28,6 +28,16 @@ public sealed partial class AnalyzerConfig
/// </summary>
internal
const
string
GlobalKey
=
"is_global"
;
/// <summary>
/// Key that indicates the precedence of this config when <see cref="IsGlobal"/> is true
/// </summary>
internal
const
string
GlobalLevelKey
=
"global_level"
;
/// <summary>
/// Filename that indicates this file is a user provided global config
/// </summary>
internal
const
string
UserGlobalConfigName
=
".globalconfig"
;
/// <summary>
/// A set of keys that are reserved for special interpretation for the editorconfig specification.
/// All values corresponding to reserved keys in a (key,value) property pair are always lowercased
...
...
@@ -86,7 +96,45 @@ public sealed partial class AnalyzerConfig
/// <summary>
/// Gets whether this editorconfig is a global editorconfig.
/// </summary>
internal
bool
IsGlobal
=>
GlobalSection
.
Properties
.
ContainsKey
(
GlobalKey
);
internal
bool
IsGlobal
=>
_hasGlobalFileName
||
GlobalSection
.
Properties
.
ContainsKey
(
GlobalKey
);
/// <summary>
/// Get the global level of this config, used to resolve conflicting keys
/// </summary>
/// <remarks>
/// A user can explicitly set the global level via the <see cref="GlobalLevelKey"/>.
/// When no global level is explicitly set, we use a heuristic:
/// <list type="bullet">
/// <item><description>
/// Any file matching the <see cref="UserGlobalConfigName"/> is determined to be a user supplied global config and gets a level of 100
/// </description></item>
/// <item><description>
/// Any other file gets a default level of 0
/// </description></item>
/// </list>
///
/// This value is unused when <see cref="IsGlobal"/> is <c>false</c>.
/// </remarks>
internal
int
GlobalLevel
{
get
{
if
(
GlobalSection
.
Properties
.
TryGetValue
(
GlobalLevelKey
,
out
string
?
val
)
&&
int
.
TryParse
(
val
,
out
int
level
))
{
return
level
;
}
else
if
(
_hasGlobalFileName
)
{
return
100
;
}
else
{
return
0
;
}
}
}
private
readonly
bool
_hasGlobalFileName
;
private
AnalyzerConfig
(
Section
globalSection
,
...
...
@@ -96,6 +144,7 @@ public sealed partial class AnalyzerConfig
GlobalSection
=
globalSection
;
NamedSections
=
namedSections
;
PathToFile
=
pathToFile
;
_hasGlobalFileName
=
Path
.
GetFileName
(
pathToFile
).
Equals
(
UserGlobalConfigName
,
StringComparison
.
OrdinalIgnoreCase
);
// Find the containing directory and normalize the path separators
string
directory
=
Path
.
GetDirectoryName
(
pathToFile
)
??
pathToFile
;
...
...
src/Compilers/Core/Portable/CommandLine/AnalyzerConfigSet.cs
浏览文件 @
80e9227e
...
...
@@ -481,8 +481,8 @@ internal static GlobalAnalyzerConfig MergeGlobalConfigs(ArrayBuilder<AnalyzerCon
/// </summary>
internal
struct
GlobalAnalyzerConfigBuilder
{
private
ImmutableDictionary
<
string
,
ImmutableDictionary
<
string
,
(
string
value
,
string
configPath
)>.
Builder
>.
Builder
?
_values
;
private
ImmutableDictionary
<
string
,
ImmutableDictionary
<
string
,
ArrayBuilder
<
string
>
>.
Builder
>.
Builder
?
_duplicates
;
private
ImmutableDictionary
<
string
,
ImmutableDictionary
<
string
,
(
string
value
,
string
configPath
,
int
globalLevel
)>.
Builder
>.
Builder
?
_values
;
private
ImmutableDictionary
<
string
,
ImmutableDictionary
<
string
,
(
int
globalLevel
,
ArrayBuilder
<
string
>
configPaths
)
>.
Builder
>.
Builder
?
_duplicates
;
internal
const
string
GlobalConfigPath
=
"<Global Config>"
;
internal
const
string
GlobalSectionName
=
"Global Section"
;
...
...
@@ -491,16 +491,16 @@ internal void MergeIntoGlobalConfig(AnalyzerConfig config, DiagnosticBag diagnos
{
if
(
_values
is
null
)
{
_values
=
ImmutableDictionary
.
CreateBuilder
<
string
,
ImmutableDictionary
<
string
,
(
string
,
string
)>.
Builder
>(
Section
.
NameEqualityComparer
);
_duplicates
=
ImmutableDictionary
.
CreateBuilder
<
string
,
ImmutableDictionary
<
string
,
ArrayBuilder
<
string
>
>.
Builder
>(
Section
.
NameEqualityComparer
);
_values
=
ImmutableDictionary
.
CreateBuilder
<
string
,
ImmutableDictionary
<
string
,
(
string
,
string
,
int
)>.
Builder
>(
Section
.
NameEqualityComparer
);
_duplicates
=
ImmutableDictionary
.
CreateBuilder
<
string
,
ImmutableDictionary
<
string
,
(
int
,
ArrayBuilder
<
string
>)
>.
Builder
>(
Section
.
NameEqualityComparer
);
}
MergeSection
(
config
.
PathToFile
,
config
.
GlobalSection
,
isGlobalSection
:
true
);
MergeSection
(
config
.
PathToFile
,
config
.
GlobalSection
,
config
.
GlobalLevel
,
isGlobalSection
:
true
);
foreach
(
var
section
in
config
.
NamedSections
)
{
if
(
IsAbsoluteEditorConfigPath
(
section
.
Name
))
{
MergeSection
(
config
.
PathToFile
,
section
,
isGlobalSection
:
false
);
MergeSection
(
config
.
PathToFile
,
section
,
config
.
GlobalLevel
,
isGlobalSection
:
false
);
}
else
{
...
...
@@ -525,7 +525,7 @@ internal GlobalAnalyzerConfig Build(DiagnosticBag diagnostics)
{
bool
isGlobalSection
=
string
.
IsNullOrWhiteSpace
(
section
);
string
sectionName
=
isGlobalSection
?
GlobalSectionName
:
section
;
foreach
((
var
keyName
,
var
configPaths
)
in
keys
)
foreach
((
var
keyName
,
(
_
,
var
configPaths
)
)
in
keys
)
{
diagnostics
.
Add
(
Diagnostic
.
Create
(
MultipleGlobalAnalyzerKeysDescriptor
,
...
...
@@ -562,52 +562,71 @@ private Section GetSection(string sectionName)
return
new
Section
(
sectionName
,
result
);
}
private
void
MergeSection
(
string
configPath
,
Section
section
,
bool
isGlobalSection
)
private
void
MergeSection
(
string
configPath
,
Section
section
,
int
globalLevel
,
bool
isGlobalSection
)
{
Debug
.
Assert
(
_values
is
object
);
Debug
.
Assert
(
_duplicates
is
object
);
if
(!
_values
.
TryGetValue
(
section
.
Name
,
out
var
sectionDict
))
{
sectionDict
=
ImmutableDictionary
.
CreateBuilder
<
string
,
(
string
,
string
)>(
Section
.
PropertiesKeyComparer
);
sectionDict
=
ImmutableDictionary
.
CreateBuilder
<
string
,
(
string
,
string
,
int
)>(
Section
.
PropertiesKeyComparer
);
_values
.
Add
(
section
.
Name
,
sectionDict
);
}
_duplicates
.
TryGetValue
(
section
.
Name
,
out
var
duplicateDict
);
foreach
((
var
key
,
var
value
)
in
section
.
Properties
)
{
if
(
isGlobalSection
&&
Section
.
PropertiesKeyComparer
.
Equals
(
key
,
GlobalKey
))
if
(
isGlobalSection
&&
(
Section
.
PropertiesKeyComparer
.
Equals
(
key
,
GlobalKey
)
||
Section
.
PropertiesKeyComparer
.
Equals
(
key
,
GlobalLevelKey
)
))
{
continue
;
}
bool
keyInSection
=
sectionDict
.
ContainsKey
(
key
);
bool
keyDuplicated
=
duplicateDict
?.
ContainsKey
(
key
)
??
false
;
bool
keyInSection
=
sectionDict
.
TryGetValue
(
key
,
out
var
sectionValue
);
// if this key is neither already present, or already duplicate, we can add it
(
int
globalLevel
,
ArrayBuilder
<
string
>
configPaths
)
duplicateValue
=
default
;
bool
keyDuplicated
=
!
keyInSection
&&
duplicateDict
?.
TryGetValue
(
key
,
out
duplicateValue
)
==
true
;
// if this key is neither already present, or already duplicate, we can add it
if
(!
keyInSection
&&
!
keyDuplicated
)
{
sectionDict
.
Add
(
key
,
(
value
,
configPath
));
sectionDict
.
Add
(
key
,
(
value
,
configPath
,
globalLevel
));
}
else
{
if
(
duplicateDict
is
null
)
int
currentGlobalLevel
=
keyInSection
?
sectionValue
.
globalLevel
:
duplicateValue
.
globalLevel
;
// if this key overrides one we knew about previously, replace it
if
(
currentGlobalLevel
<
globalLevel
)
{
duplicateDict
=
ImmutableDictionary
.
CreateBuilder
<
string
,
ArrayBuilder
<
string
>>(
Section
.
PropertiesKeyComparer
);
_duplicates
.
Add
(
section
.
Name
,
duplicateDict
);
sectionDict
[
key
]
=
(
value
,
configPath
,
globalLevel
);
if
(
keyDuplicated
)
{
duplicateDict
!.
Remove
(
key
);
}
}
// this key conflicts with a previous one
else
if
(
currentGlobalLevel
==
globalLevel
)
{
if
(
duplicateDict
is
null
)
{
duplicateDict
=
ImmutableDictionary
.
CreateBuilder
<
string
,
(
int
,
ArrayBuilder
<
string
>)>(
Section
.
PropertiesKeyComparer
);
_duplicates
.
Add
(
section
.
Name
,
duplicateDict
);
}
// record that this key is now a duplicate
ArrayBuilder
<
string
>
configList
=
keyDuplicated
?
duplicateDict
[
key
]
:
ArrayBuilder
<
string
>.
GetInstance
();
configList
.
Add
(
configPath
);
duplicateDict
[
key
]
=
configList
;
// record that this key is now a duplicate
ArrayBuilder
<
string
>
configList
=
duplicateValue
.
configPaths
??
ArrayBuilder
<
string
>.
GetInstance
();
configList
.
Add
(
configPath
);
duplicateDict
[
key
]
=
(
globalLevel
,
configList
)
;
// if we'd previously added this key, remove it and remember the extra duplicate location
if
(
keyInSection
)
{
var
originalConfigPath
=
sectionDict
[
key
].
configPath
;
sectionDict
.
Remove
(
key
);
duplicateDict
[
key
].
Insert
(
0
,
originalConfigPath
);
// if we'd previously added this key, remove it and remember the extra duplicate location
if
(
keyInSection
)
{
var
originalEntry
=
sectionValue
;
Debug
.
Assert
(
originalEntry
.
globalLevel
==
globalLevel
);
sectionDict
.
Remove
(
key
);
configList
.
Insert
(
0
,
originalEntry
.
configPath
);
}
}
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录