HostAnalyzerManager.cs 23.7 KB
Newer Older
H
heejaechang 已提交
1 2 3 4 5 6 7
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
8
using System.Runtime.CompilerServices;
9
using Microsoft.CodeAnalysis.Diagnostics.Log;
H
heejaechang 已提交
10 11 12 13 14 15 16 17 18 19 20
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Diagnostics
{
    /// <summary>
    /// This owns every information about analyzer itself 
    /// such as <see cref="AnalyzerReference"/>, <see cref="DiagnosticAnalyzer"/> and <see cref="DiagnosticDescriptor"/>.
    /// 
    /// people should use this to get <see cref="DiagnosticAnalyzer"/>s or <see cref="DiagnosticDescriptor"/>s of a <see cref="AnalyzerReference"/>.
    /// this will do appropriate de-duplication and cache for those information.
    /// 
C
Charles Stoner 已提交
21
    /// this should be always thread-safe.
H
heejaechang 已提交
22
    /// </summary>
23
    internal sealed partial class HostAnalyzerManager
H
heejaechang 已提交
24
    {
H
Heejae Chang 已提交
25 26 27
        /// <summary>
        /// This contains vsix info on where <see cref="HostDiagnosticAnalyzerPackage"/> comes from.
        /// </summary>
28
        private readonly Lazy<ImmutableArray<HostDiagnosticAnalyzerPackage>> _hostDiagnosticAnalyzerPackages;
H
Heejae Chang 已提交
29

H
heejaechang 已提交
30 31 32 33 34
        /// <summary>
        /// Key is analyzer reference identity <see cref="GetAnalyzerReferenceIdentity(AnalyzerReference)"/>.
        /// 
        /// We use the key to de-duplicate analyzer references if they are referenced from multiple places.
        /// </summary>
35
        private readonly Lazy<ImmutableDictionary<object, AnalyzerReference>> _hostAnalyzerReferencesMap;
H
heejaechang 已提交
36 37 38 39 40 41 42

        /// <summary>
        /// Key is the language the <see cref="DiagnosticAnalyzer"/> supports and key for the second map is analyzer reference identity and
        /// <see cref="DiagnosticAnalyzer"/> for that assembly reference.
        /// 
        /// Entry will be lazily filled in.
        /// </summary>
43
        private readonly ConcurrentDictionary<string, ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>>> _hostDiagnosticAnalyzersPerLanguageMap;
H
heejaechang 已提交
44

45 46 47 48 49 50 51
        /// <summary>
        /// Key is analyzer reference identity <see cref="GetAnalyzerReferenceIdentity(AnalyzerReference)"/>.
        /// 
        /// Value is set of <see cref="DiagnosticAnalyzer"/> that belong to the <see cref="AnalyzerReference"/>.
        /// 
        /// We populate it lazily. otherwise, we will bring in all analyzers preemptively
        /// </summary>
52
        private readonly Lazy<ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>>> _lazyHostDiagnosticAnalyzersPerReferenceMap;
53

M
Manish Vasani 已提交
54 55 56 57 58
        /// <summary>
        /// Host diagnostic update source for analyzer host specific diagnostics.
        /// </summary>
        private readonly AbstractHostDiagnosticUpdateSource _hostDiagnosticUpdateSource;

59 60 61 62 63
        /// <summary>
        /// map to compiler diagnostic analyzer.
        /// </summary>
        private ImmutableDictionary<string, DiagnosticAnalyzer> _compilerDiagnosticAnalyzerMap;

H
Heejae Chang 已提交
64 65 66
        /// <summary>
        /// map from host diagnostic analyzer to package name it came from
        /// </summary>
C
Charles Stoner 已提交
67
        private ImmutableDictionary<DiagnosticAnalyzer, string> _hostDiagnosticAnalyzerPackageNameMap;
H
Heejae Chang 已提交
68

69 70 71 72 73
        /// <summary>
        /// map to compiler diagnostic analyzer descriptor.
        /// </summary>
        private ImmutableDictionary<DiagnosticAnalyzer, HashSet<string>> _compilerDiagnosticAnalyzerDescriptorMap;

74
        /// <summary>
75
        /// Cache from <see cref="DiagnosticAnalyzer"/> instance to its supported descriptors.
76 77 78
        /// </summary>
        private readonly ConditionalWeakTable<DiagnosticAnalyzer, IReadOnlyCollection<DiagnosticDescriptor>> _descriptorCache;

79 80 81
        public HostAnalyzerManager(Lazy<ImmutableArray<HostDiagnosticAnalyzerPackage>> hostAnalyzerPackages, IAnalyzerAssemblyLoader hostAnalyzerAssemblyLoader, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, PrimaryWorkspace primaryWorkspace) :
            this(new Lazy<ImmutableArray<AnalyzerReference>>(() => CreateAnalyzerReferencesFromPackages(hostAnalyzerPackages.Value, new HostAnalyzerReferenceDiagnosticReporter(hostDiagnosticUpdateSource, primaryWorkspace), hostAnalyzerAssemblyLoader), isThreadSafe: true),
                 hostAnalyzerPackages, hostDiagnosticUpdateSource)
H
heejaechang 已提交
82
        {
H
Heejae Chang 已提交
83 84 85
        }

        private HostAnalyzerManager(
86
            Lazy<ImmutableArray<AnalyzerReference>> hostAnalyzerReferences, Lazy<ImmutableArray<HostDiagnosticAnalyzerPackage>> hostAnalyzerPackages, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource)
H
Heejae Chang 已提交
87 88
        {
            _hostDiagnosticAnalyzerPackages = hostAnalyzerPackages;
89 90
            _hostDiagnosticUpdateSource = hostDiagnosticUpdateSource;

91
            _hostAnalyzerReferencesMap = new Lazy<ImmutableDictionary<object, AnalyzerReference>>(() => hostAnalyzerReferences.Value.IsDefaultOrEmpty ? ImmutableDictionary<object, AnalyzerReference>.Empty : CreateAnalyzerReferencesMap(hostAnalyzerReferences.Value), isThreadSafe: true);
92
            _hostDiagnosticAnalyzersPerLanguageMap = new ConcurrentDictionary<string, ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>>>(concurrencyLevel: 2, capacity: 2);
93
            _lazyHostDiagnosticAnalyzersPerReferenceMap = new Lazy<ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>>>(() => CreateDiagnosticAnalyzersPerReferenceMap(_hostAnalyzerReferencesMap.Value), isThreadSafe: true);
M
Manish Vasani 已提交
94

95 96
            _compilerDiagnosticAnalyzerMap = ImmutableDictionary<string, DiagnosticAnalyzer>.Empty;
            _compilerDiagnosticAnalyzerDescriptorMap = ImmutableDictionary<DiagnosticAnalyzer, HashSet<string>>.Empty;
C
Charles Stoner 已提交
97
            _hostDiagnosticAnalyzerPackageNameMap = ImmutableDictionary<DiagnosticAnalyzer, string>.Empty;
98
            _descriptorCache = new ConditionalWeakTable<DiagnosticAnalyzer, IReadOnlyCollection<DiagnosticDescriptor>>();
99
        }
100

101 102 103 104
        // this is for testing
        internal HostAnalyzerManager(ImmutableArray<AnalyzerReference> hostAnalyzerReferences, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) :
            this(new Lazy<ImmutableArray<AnalyzerReference>>(() => hostAnalyzerReferences), new Lazy<ImmutableArray<HostDiagnosticAnalyzerPackage>>(() => ImmutableArray<HostDiagnosticAnalyzerPackage>.Empty), hostDiagnosticUpdateSource)
        {
H
heejaechang 已提交
105 106 107 108 109
        }

        /// <summary>
        /// It returns a string that can be used as a way to de-duplicate <see cref="AnalyzerReference"/>s.
        /// </summary>
110
        public object GetAnalyzerReferenceIdentity(AnalyzerReference reference)
H
heejaechang 已提交
111
        {
T
Tom Meschter 已提交
112
            return reference.Id;
H
heejaechang 已提交
113 114
        }

115 116 117 118 119 120 121
        /// <summary>
        /// It returns a map with <see cref="AnalyzerReference.Id"/> as key and <see cref="AnalyzerReference"/> as value
        /// </summary>
        public ImmutableDictionary<object, AnalyzerReference> CreateAnalyzerReferencesMap(Project projectOpt = null)
        {
            if (projectOpt == null)
            {
122
                return _hostAnalyzerReferencesMap.Value;
123 124
            }

125
            return _hostAnalyzerReferencesMap.Value.AddRange(CreateProjectAnalyzerReferencesMap(projectOpt));
126 127
        }

H
heejaechang 已提交
128 129 130 131
        /// <summary>
        /// Return <see cref="DiagnosticAnalyzer.SupportedDiagnostics"/> of given <paramref name="analyzer"/>.
        /// </summary>
        public ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptors(DiagnosticAnalyzer analyzer)
132 133 134 135 136
        {
            return (ImmutableArray<DiagnosticDescriptor>)_descriptorCache.GetValue(analyzer, a => GetDiagnosticDescriptorsCore(a));
        }

        private ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptorsCore(DiagnosticAnalyzer analyzer)
H
heejaechang 已提交
137
        {
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
            ImmutableArray<DiagnosticDescriptor> descriptors;
            try
            {
                // SupportedDiagnostics is user code and can throw an exception.
                descriptors = analyzer.SupportedDiagnostics;
                if (descriptors.IsDefault)
                {
                    descriptors = ImmutableArray<DiagnosticDescriptor>.Empty;
                }
            }
            catch (Exception ex)
            {
                AnalyzerHelper.OnAnalyzerExceptionForSupportedDiagnostics(analyzer, ex, _hostDiagnosticUpdateSource);
                descriptors = ImmutableArray<DiagnosticDescriptor>.Empty;
            }

            return descriptors;
        }

        /// <summary>
        /// Return true if the given <paramref name="analyzer"/> is suppressed for the given project.
        /// </summary>
        public bool IsAnalyzerSuppressed(DiagnosticAnalyzer analyzer, Project project)
        {
            var options = project.CompilationOptions;
163
            if (options == null || IsCompilerDiagnosticAnalyzer(project.Language, analyzer))
164 165 166 167
            {
                return false;
            }

H
Heejae Chang 已提交
168 169 170
            // don't capture project
            var projectId = project.Id;

171
            // Skip telemetry logging for supported diagnostics, as that can cause an infinite loop.
C
CyrusNajmabadi 已提交
172
            void onAnalyzerException(Exception ex, DiagnosticAnalyzer a, Diagnostic diagnostic) =>
H
Heejae Chang 已提交
173
                    AnalyzerHelper.OnAnalyzerException_NoTelemetryLogging(ex, a, diagnostic, _hostDiagnosticUpdateSource, projectId);
174 175

            return CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(analyzer, options, onAnalyzerException);
H
heejaechang 已提交
176 177 178
        }

        /// <summary>
179
        /// Get <see cref="AnalyzerReference"/> identity and <see cref="DiagnosticAnalyzer"/>s map for given <paramref name="language"/>
M
Manish Vasani 已提交
180
        /// </summary> 
181
        public ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> GetHostDiagnosticAnalyzersPerReference(string language)
182
        {
H
Heejae Chang 已提交
183
            return _hostDiagnosticAnalyzersPerLanguageMap.GetOrAdd(language, CreateHostDiagnosticAnalyzersAndBuildMap);
184 185 186 187
        }

        /// <summary>
        /// Create <see cref="AnalyzerReference"/> identity and <see cref="DiagnosticDescriptor"/>s map
H
heejaechang 已提交
188
        /// </summary>
189
        public ImmutableDictionary<object, ImmutableArray<DiagnosticDescriptor>> GetHostDiagnosticDescriptorsPerReference()
H
heejaechang 已提交
190
        {
191
            return CreateDiagnosticDescriptorsPerReference(_lazyHostDiagnosticAnalyzersPerReferenceMap.Value, projectOpt: null);
H
heejaechang 已提交
192 193 194
        }

        /// <summary>
195
        /// Create <see cref="AnalyzerReference"/> identity and <see cref="DiagnosticDescriptor"/>s map for given <paramref name="project"/>
H
heejaechang 已提交
196
        /// </summary>
197
        public ImmutableDictionary<object, ImmutableArray<DiagnosticDescriptor>> CreateDiagnosticDescriptorsPerReference(Project project)
198
        {
199
            return CreateDiagnosticDescriptorsPerReference(CreateDiagnosticAnalyzersPerReference(project), project);
200 201 202
        }

        /// <summary>
203 204
        /// Create <see cref="AnalyzerReference"/> identity and <see cref="DiagnosticAnalyzer"/>s map for given <paramref name="project"/> that
        /// includes both host and project analyzers
205
        /// </summary>
206
        public ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> CreateDiagnosticAnalyzersPerReference(Project project)
H
heejaechang 已提交
207 208
        {
            var hostAnalyzerReferences = GetHostDiagnosticAnalyzersPerReference(project.Language);
209
            var projectAnalyzerReferences = CreateProjectDiagnosticAnalyzersPerReference(project);
H
heejaechang 已提交
210

211
            return MergeDiagnosticAnalyzerMap(hostAnalyzerReferences, projectAnalyzerReferences);
H
heejaechang 已提交
212 213
        }

214 215 216 217
        /// <summary>
        /// Create <see cref="AnalyzerReference"/> identity and <see cref="DiagnosticAnalyzer"/>s map for given <paramref name="project"/> that
        /// has only project analyzers
        /// </summary>
218
        public ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> CreateProjectDiagnosticAnalyzersPerReference(Project project)
219
        {
220
            return CreateDiagnosticAnalyzersPerReferenceMap(CreateProjectAnalyzerReferencesMap(project), project.Language);
221 222
        }

223 224 225 226 227 228
        /// <summary>
        /// Check whether given <see cref="DiagnosticData"/> belong to compiler diagnostic analyzer
        /// </summary>
        public bool IsCompilerDiagnostic(string language, DiagnosticData diagnostic)
        {
            var map = GetHostDiagnosticAnalyzersPerReference(language);
C
CyrusNajmabadi 已提交
229 230
            if (_compilerDiagnosticAnalyzerMap.TryGetValue(language, out var compilerAnalyzer) &&
                _compilerDiagnosticAnalyzerDescriptorMap.TryGetValue(compilerAnalyzer, out var idMap) &&
231 232 233 234 235 236 237 238
                idMap.Contains(diagnostic.Id))
            {
                return true;
            }

            return false;
        }

H
Heejae Chang 已提交
239 240 241 242 243 244
        /// <summary>
        /// Return compiler <see cref="DiagnosticAnalyzer"/> for the given language.
        /// </summary>
        public DiagnosticAnalyzer GetCompilerDiagnosticAnalyzer(string language)
        {
            var map = GetHostDiagnosticAnalyzersPerReference(language);
C
CyrusNajmabadi 已提交
245
            if (_compilerDiagnosticAnalyzerMap.TryGetValue(language, out var compilerAnalyzer))
H
Heejae Chang 已提交
246 247 248 249 250 251 252 253 254 255 256 257 258
            {
                return compilerAnalyzer;
            }

            return null;
        }

        /// <summary>
        /// Check whether given <see cref="DiagnosticAnalyzer"/> is compiler analyzer for the language or not.
        /// </summary>
        public bool IsCompilerDiagnosticAnalyzer(string language, DiagnosticAnalyzer analyzer)
        {
            var map = GetHostDiagnosticAnalyzersPerReference(language);
C
CyrusNajmabadi 已提交
259
            return _compilerDiagnosticAnalyzerMap.TryGetValue(language, out var compilerAnalyzer) && compilerAnalyzer == analyzer;
H
Heejae Chang 已提交
260 261 262 263 264 265 266 267
        }

        /// <summary>
        /// Get Name of Package (vsix) which Host <see cref="DiagnosticAnalyzer"/> is from.
        /// </summary>
        public string GetDiagnosticAnalyzerPackageName(string language, DiagnosticAnalyzer analyzer)
        {
            var map = GetHostDiagnosticAnalyzersPerReference(language);
C
CyrusNajmabadi 已提交
268
            if (_hostDiagnosticAnalyzerPackageNameMap.TryGetValue(analyzer, out var name))
H
Heejae Chang 已提交
269 270 271 272 273 274 275
            {
                return name;
            }

            return null;
        }

276 277 278 279 280
        private ImmutableDictionary<object, AnalyzerReference> CreateProjectAnalyzerReferencesMap(Project project)
        {
            return CreateAnalyzerReferencesMap(project.AnalyzerReferences.Where(CheckAnalyzerReferenceIdentity));
        }

281
        private ImmutableDictionary<object, ImmutableArray<DiagnosticDescriptor>> CreateDiagnosticDescriptorsPerReference(
282 283
            ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> analyzersMap,
            Project projectOpt)
H
heejaechang 已提交
284
        {
285
            var builder = ImmutableDictionary.CreateBuilder<object, ImmutableArray<DiagnosticDescriptor>>();
H
heejaechang 已提交
286 287 288 289 290 291 292 293
            foreach (var kv in analyzersMap)
            {
                var referenceId = kv.Key;
                var analyzers = kv.Value;

                var descriptors = ImmutableArray.CreateBuilder<DiagnosticDescriptor>();
                foreach (var analyzer in analyzers)
                {
294
                    // given map should be in good shape. no duplication. no null and etc
H
heejaechang 已提交
295 296 297
                    descriptors.AddRange(GetDiagnosticDescriptors(analyzer));
                }

298
                // there can't be duplication since _hostAnalyzerReferenceMap is already de-duplicated.
H
heejaechang 已提交
299 300 301 302 303 304
                builder.Add(referenceId, descriptors.ToImmutable());
            }

            return builder.ToImmutable();
        }

305
        private ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> CreateHostDiagnosticAnalyzersAndBuildMap(string language)
H
heejaechang 已提交
306 307 308
        {
            Contract.ThrowIfNull(language);

H
Heejae Chang 已提交
309 310
            var nameMap = CreateAnalyzerPathToPackageNameMap();

311
            var builder = ImmutableDictionary.CreateBuilder<object, ImmutableArray<DiagnosticAnalyzer>>();
312
            foreach (var kv in _hostAnalyzerReferencesMap.Value)
H
heejaechang 已提交
313
            {
C
Charles Stoner 已提交
314
                var referenceIdentity = kv.Key;
H
heejaechang 已提交
315 316 317 318 319 320 321 322
                var reference = kv.Value;

                var analyzers = reference.GetAnalyzers(language);
                if (analyzers.Length == 0)
                {
                    continue;
                }

323 324
                UpdateCompilerAnalyzerMapIfNeeded(language, analyzers);

H
Heejae Chang 已提交
325 326
                UpdateDiagnosticAnalyzerToPackageNameMap(nameMap, reference, analyzers);

H
heejaechang 已提交
327
                // there can't be duplication since _hostAnalyzerReferenceMap is already de-duplicated.
C
Charles Stoner 已提交
328
                builder.Add(referenceIdentity, analyzers);
H
heejaechang 已提交
329 330 331 332 333
            }

            return builder.ToImmutable();
        }

H
Heejae Chang 已提交
334 335 336 337 338 339 340 341 342 343 344
        private void UpdateDiagnosticAnalyzerToPackageNameMap(
            ImmutableDictionary<string, string> nameMap,
            AnalyzerReference reference,
            ImmutableArray<DiagnosticAnalyzer> analyzers)
        {
            var fileReference = reference as AnalyzerFileReference;
            if (fileReference == null)
            {
                return;
            }

C
CyrusNajmabadi 已提交
345
            if (!nameMap.TryGetValue(fileReference.FullPath, out var name))
H
Heejae Chang 已提交
346 347 348 349 350 351
            {
                return;
            }

            foreach (var analyzer in analyzers)
            {
352
                ImmutableInterlocked.GetOrAdd(ref _hostDiagnosticAnalyzerPackageNameMap, analyzer, name);
H
Heejae Chang 已提交
353 354 355 356 357 358
            }
        }

        private ImmutableDictionary<string, string> CreateAnalyzerPathToPackageNameMap()
        {
            var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
359
            foreach (var package in _hostDiagnosticAnalyzerPackages.Value)
H
Heejae Chang 已提交
360
            {
361 362 363 364 365
                if (string.IsNullOrEmpty(package.Name))
                {
                    continue;
                }

H
Heejae Chang 已提交
366 367 368 369 370 371 372 373 374 375 376 377
                foreach (var assembly in package.Assemblies)
                {
                    if (!builder.ContainsKey(assembly))
                    {
                        builder.Add(assembly, package.Name);
                    }
                }
            }

            return builder.ToImmutable();
        }

378 379 380 381 382 383 384 385 386 387 388 389
        private void UpdateCompilerAnalyzerMapIfNeeded(string language, ImmutableArray<DiagnosticAnalyzer> analyzers)
        {
            if (_compilerDiagnosticAnalyzerMap.ContainsKey(language))
            {
                return;
            }

            foreach (var analyzer in analyzers)
            {
                if (analyzer.IsCompilerAnalyzer())
                {
                    ImmutableInterlocked.GetOrAdd(ref _compilerDiagnosticAnalyzerDescriptorMap, analyzer, a => new HashSet<string>(GetDiagnosticDescriptors(a).Select(d => d.Id)));
390
                    ImmutableInterlocked.GetOrAdd(ref _compilerDiagnosticAnalyzerMap, language, analyzer);
391 392 393 394 395
                    return;
                }
            }
        }

396 397 398 399 400 401 402
        private bool CheckAnalyzerReferenceIdentity(AnalyzerReference reference)
        {
            if (reference == null)
            {
                return false;
            }

403
            return !_hostAnalyzerReferencesMap.Value.ContainsKey(reference.Id);
404 405
        }

406 407
        private static ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> CreateDiagnosticAnalyzersPerReferenceMap(
            IDictionary<object, AnalyzerReference> analyzerReferencesMap, string languageOpt = null)
H
heejaechang 已提交
408
        {
409
            var builder = ImmutableDictionary.CreateBuilder<object, ImmutableArray<DiagnosticAnalyzer>>();
H
heejaechang 已提交
410 411 412 413 414 415 416 417 418 419

            foreach (var reference in analyzerReferencesMap)
            {
                var analyzers = languageOpt == null ? reference.Value.GetAnalyzersForAllLanguages() : reference.Value.GetAnalyzers(languageOpt);
                if (analyzers.Length == 0)
                {
                    continue;
                }

                // input "analyzerReferencesMap" is a dictionary, so there will be no duplication here.
420
                builder.Add(reference.Key, analyzers.WhereNotNull().ToImmutableArray());
H
heejaechang 已提交
421 422 423 424 425
            }

            return builder.ToImmutable();
        }

426
        private static ImmutableDictionary<object, AnalyzerReference> CreateAnalyzerReferencesMap(IEnumerable<AnalyzerReference> analyzerReferences)
H
heejaechang 已提交
427
        {
428
            var builder = ImmutableDictionary.CreateBuilder<object, AnalyzerReference>();
H
heejaechang 已提交
429 430
            foreach (var reference in analyzerReferences)
            {
T
Tom Meschter 已提交
431
                var key = reference.Id;
H
heejaechang 已提交
432 433 434 435 436 437 438 439 440 441 442 443 444

                // filter out duplicated analyzer reference
                if (builder.ContainsKey(key))
                {
                    continue;
                }

                builder.Add(key, reference);
            }

            return builder.ToImmutable();
        }

445
        private static ImmutableArray<AnalyzerReference> CreateAnalyzerReferencesFromPackages(
446
            ImmutableArray<HostDiagnosticAnalyzerPackage> analyzerPackages,
447 448
            HostAnalyzerReferenceDiagnosticReporter reporter,
            IAnalyzerAssemblyLoader hostAnalyzerAssemblyLoader)
H
heejaechang 已提交
449
        {
450
            if (analyzerPackages.IsEmpty)
H
heejaechang 已提交
451 452 453 454
            {
                return ImmutableArray<AnalyzerReference>.Empty;
            }

455 456
            Contract.ThrowIfNull(hostAnalyzerAssemblyLoader);

H
Heejae Chang 已提交
457 458
            var analyzerAssemblies = analyzerPackages.SelectMany(p => p.Assemblies);

H
heejaechang 已提交
459 460 461
            var builder = ImmutableArray.CreateBuilder<AnalyzerReference>();
            foreach (var analyzerAssembly in analyzerAssemblies.Distinct(StringComparer.OrdinalIgnoreCase))
            {
462
                var reference = new AnalyzerFileReference(analyzerAssembly, hostAnalyzerAssemblyLoader);
463 464 465
                reference.AnalyzerLoadFailed += reporter.OnAnalyzerLoadFailed;

                builder.Add(reference);
H
heejaechang 已提交
466 467
            }

468 469 470 471
            var references = builder.ToImmutable();
            DiagnosticAnalyzerLogger.LogWorkspaceAnalyzers(references);

            return references;
H
heejaechang 已提交
472
        }
473

474 475
        private static ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> MergeDiagnosticAnalyzerMap(
            ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> map1, ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> map2)
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
        {
            var current = map1;
            var seen = new HashSet<DiagnosticAnalyzer>(map1.Values.SelectMany(v => v));

            foreach (var kv in map2)
            {
                var referenceIdentity = kv.Key;
                var analyzers = kv.Value;

                if (map1.ContainsKey(referenceIdentity))
                {
                    continue;
                }

                current = current.Add(referenceIdentity, analyzers.Where(a => seen.Add(a)).ToImmutableArray());
            }

            return current;
        }
T
Tom Meschter 已提交
495

496 497 498
        private class HostAnalyzerReferenceDiagnosticReporter
        {
            private readonly AbstractHostDiagnosticUpdateSource _hostUpdateSource;
499
            private readonly PrimaryWorkspace _primaryWorkspace;
500

501
            public HostAnalyzerReferenceDiagnosticReporter(AbstractHostDiagnosticUpdateSource hostUpdateSource, PrimaryWorkspace primaryWorkspace)
502 503
            {
                _hostUpdateSource = hostUpdateSource;
504
                _primaryWorkspace = primaryWorkspace;
505 506 507 508 509 510 511 512 513 514 515 516 517
            }

            public void OnAnalyzerLoadFailed(object sender, AnalyzerLoadFailureEventArgs e)
            {
                var reference = sender as AnalyzerFileReference;
                if (reference == null)
                {
                    return;
                }

                var diagnostic = AnalyzerHelper.CreateAnalyzerLoadFailureDiagnostic(reference.FullPath, e);

                // diagnostic from host analyzer can never go away
518
                var args = DiagnosticsUpdatedArgs.DiagnosticsCreated(
519
                    id: Tuple.Create(this, reference.FullPath, e.ErrorCode, e.TypeName),
520
                    workspace: _primaryWorkspace.Workspace,
521 522 523 524 525
                    solution: null,
                    projectId: null,
                    documentId: null,
                    diagnostics: ImmutableArray.Create<DiagnosticData>(diagnostic));

H
Heejae Chang 已提交
526 527
                // this can be null in test. but in product code, this should never be null.
                _hostUpdateSource?.RaiseDiagnosticsUpdated(args);
528 529
            }
        }
H
heejaechang 已提交
530 531
    }
}