AnalyzerHelper.cs 10.0 KB
Newer Older
H
heejaechang 已提交
1 2
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

M
Manish Vasani 已提交
3
using System;
4
using System.Reflection;
M
Manish Vasani 已提交
5
using Microsoft.CodeAnalysis.ErrorReporting;
6
using Roslyn.Utilities;
M
Manish Vasani 已提交
7

H
heejaechang 已提交
8 9 10 11 12 13 14
namespace Microsoft.CodeAnalysis.Diagnostics
{
    internal static class AnalyzerHelper
    {
        private const string CSharpCompilerAnalyzerTypeName = "Microsoft.CodeAnalysis.Diagnostics.CSharp.CSharpCompilerDiagnosticAnalyzer";
        private const string VisualBasicCompilerAnalyzerTypeName = "Microsoft.CodeAnalysis.Diagnostics.VisualBasic.VisualBasicCompilerDiagnosticAnalyzer";

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
        // These are the error codes of the compiler warnings. 
        // Keep the ids the same so that de-duplication against compiler errors
        // works in the error list (after a build).
        internal const string WRN_AnalyzerCannotBeCreatedIdCS = "CS8032";
        internal const string WRN_AnalyzerCannotBeCreatedIdVB = "BC42376";
        internal const string WRN_NoAnalyzerInAssemblyIdCS = "CS8033";
        internal const string WRN_NoAnalyzerInAssemblyIdVB = "BC42377";
        internal const string WRN_UnableToLoadAnalyzerIdCS = "CS8034";
        internal const string WRN_UnableToLoadAnalyzerIdVB = "BC42378";

        // Shared with Compiler
        internal const string AnalyzerExceptionDiagnosticId = "AD0001";
        internal const string AnalyzerDriverExceptionDiagnosticId = "AD0002";

        // IDE only errors
        internal const string WRN_AnalyzerCannotBeCreatedId = "AD1000";
        internal const string WRN_NoAnalyzerInAssemblyId = "AD1001";
        internal const string WRN_UnableToLoadAnalyzerId = "AD1002";

34 35
        private const string AnalyzerExceptionDiagnosticCategory = "Intellisense";

36
        public static bool IsBuiltInAnalyzer(this DiagnosticAnalyzer analyzer)
H
heejaechang 已提交
37
        {
38
            return analyzer is IBuiltInAnalyzer || analyzer is DocumentDiagnosticAnalyzer || analyzer is ProjectDiagnosticAnalyzer || analyzer.IsCompilerAnalyzer();
H
heejaechang 已提交
39 40
        }

41
        public static bool IsCompilerAnalyzer(this DiagnosticAnalyzer analyzer)
H
heejaechang 已提交
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
        {
            // TODO: find better way.
            var typeString = analyzer.GetType().ToString();
            if (typeString == CSharpCompilerAnalyzerTypeName)
            {
                return true;
            }

            if (typeString == VisualBasicCompilerAnalyzerTypeName)
            {
                return true;
            }

            return false;
        }
M
Manish Vasani 已提交
57

58
        public static ValueTuple<string, VersionStamp> GetAnalyzerIdAndVersion(this DiagnosticAnalyzer analyzer)
59 60 61 62
        {
            // Get the unique ID for given diagnostic analyzer.
            // note that we also put version stamp so that we can detect changed analyzer.
            var type = analyzer.GetType();
63 64
            var typeInfo = type.GetTypeInfo();
            return ValueTuple.Create(GetAssemblyQualifiedName(type), GetAnalyzerVersion(CorLightup.Desktop.GetAssemblyLocation(typeInfo.Assembly)));
65 66
        }

H
Heejae Chang 已提交
67 68
        public static string GetAnalyzerAssemblyName(this DiagnosticAnalyzer analyzer)
        {
69 70
            var typeInfo = analyzer.GetType().GetTypeInfo();
            return typeInfo.Assembly.GetName().Name;
H
Heejae Chang 已提交
71 72
        }

73
        private static string GetAssemblyQualifiedName(Type type)
74
        {
75 76 77
            // AnalyzerFileReference now includes things like versions, public key as part of its identity. 
            // so we need to consider them.
            return type.AssemblyQualifiedName;
78 79
        }

80
        internal static void OnAnalyzerException_NoTelemetryLogging(
81
            Exception ex,
M
Manish Vasani 已提交
82
            DiagnosticAnalyzer analyzer,
83
            Diagnostic diagnostic,
M
Manish Vasani 已提交
84
            AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource,
85
            ProjectId projectIdOpt)
M
Manish Vasani 已提交
86
        {
87
            if (diagnostic != null)
88
            {
89
                hostDiagnosticUpdateSource?.ReportAnalyzerDiagnostic(analyzer, diagnostic, hostDiagnosticUpdateSource?.Workspace, projectIdOpt);
90
            }
M
Manish Vasani 已提交
91

92
            if (IsBuiltInAnalyzer(analyzer))
93
            {
94 95 96 97 98 99 100 101 102
                FatalError.ReportWithoutCrashUnlessCanceled(ex);
            }
        }

        internal static void OnAnalyzerExceptionForSupportedDiagnostics(DiagnosticAnalyzer analyzer, Exception exception, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource)
        {
            if (exception is OperationCanceledException)
            {
                return;
103
            }
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

            var diagnostic = CreateAnalyzerExceptionDiagnostic(analyzer, exception);
            OnAnalyzerException_NoTelemetryLogging(exception, analyzer, diagnostic, hostDiagnosticUpdateSource, projectIdOpt: null);
        }

        /// <summary>
        /// Create a diagnostic for exception thrown by the given analyzer.
        /// </summary>
        /// <remarks>
        /// Keep this method in sync with "AnalyzerExecutor.CreateAnalyzerExceptionDiagnostic".
        /// </remarks>
        internal static Diagnostic CreateAnalyzerExceptionDiagnostic(DiagnosticAnalyzer analyzer, Exception e)
        {
            var analyzerName = analyzer.ToString();

            // TODO: It is not ideal to create a new descriptor per analyzer exception diagnostic instance.
            // However, until we add a LongMessage field to the Diagnostic, we are forced to park the instance specific description onto the Descriptor's Description field.
            // This requires us to create a new DiagnosticDescriptor instance per diagnostic instance.
            var descriptor = new DiagnosticDescriptor(AnalyzerExceptionDiagnosticId,
123 124
                title: FeaturesResources.UserDiagnosticAnalyzerFailure,
                messageFormat: FeaturesResources.UserDiagnosticAnalyzerThrows,
125
                description: string.Format(FeaturesResources.UserDiagnosticAnalyzerThrowsDescription, analyzerName, e.CreateDiagnosticDescription()),
126
                category: AnalyzerExceptionDiagnosticCategory,
127
                defaultSeverity: DiagnosticSeverity.Warning,
128 129 130 131
                isEnabledByDefault: true,
                customTags: WellKnownDiagnosticTags.AnalyzerException);

            return Diagnostic.Create(descriptor, Location.None, analyzerName, e.GetType(), e.Message);
M
Manish Vasani 已提交
132
        }
133

H
Heejae Chang 已提交
134
        private static VersionStamp GetAnalyzerVersion(string path)
135
        {
136
            if (path == null || !PortableShim.File.Exists(path))
137 138 139 140
            {
                return VersionStamp.Default;
            }

141
            return VersionStamp.Create(PortableShim.File.GetLastWriteTimeUtc(path));
142
        }
143 144 145 146 147 148 149 150 151

        public static DiagnosticData CreateAnalyzerLoadFailureDiagnostic(string fullPath, AnalyzerLoadFailureEventArgs e)
        {
            return CreateAnalyzerLoadFailureDiagnostic(null, null, null, fullPath, e);
        }

        public static DiagnosticData CreateAnalyzerLoadFailureDiagnostic(
            Workspace workspace, ProjectId projectId, string language, string fullPath, AnalyzerLoadFailureEventArgs e)
        {
152 153
            string id, message, messageFormat, description;
            if (!TryGetErrorMessage(language, fullPath, e, out id, out message, out messageFormat, out description))
154 155 156 157 158 159 160 161 162 163 164
            {
                return null;
            }

            return new DiagnosticData(
                id,
                FeaturesResources.ErrorCategory,
                message,
                messageFormat,
                severity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true,
165
                description: description,
166 167 168 169 170 171 172
                warningLevel: 0,
                workspace: workspace,
                projectId: projectId);
        }

        private static bool TryGetErrorMessage(
            string language, string fullPath, AnalyzerLoadFailureEventArgs e,
173
            out string id, out string message, out string messageFormat, out string description)
174 175 176 177 178 179 180
        {
            switch (e.ErrorCode)
            {
                case AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToLoadAnalyzer:
                    id = Choose(language, WRN_UnableToLoadAnalyzerId, WRN_UnableToLoadAnalyzerIdCS, WRN_UnableToLoadAnalyzerIdVB);
                    messageFormat = FeaturesResources.WRN_UnableToLoadAnalyzer;
                    message = string.Format(FeaturesResources.WRN_UnableToLoadAnalyzer, fullPath, e.Message);
181
                    description = e.Exception.CreateDiagnosticDescription();
182 183 184 185 186
                    break;
                case AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer:
                    id = Choose(language, WRN_AnalyzerCannotBeCreatedId, WRN_AnalyzerCannotBeCreatedIdCS, WRN_AnalyzerCannotBeCreatedIdVB);
                    messageFormat = FeaturesResources.WRN_AnalyzerCannotBeCreated;
                    message = string.Format(FeaturesResources.WRN_AnalyzerCannotBeCreated, e.TypeName, fullPath, e.Message);
187
                    description = e.Exception.CreateDiagnosticDescription();
188 189 190 191 192
                    break;
                case AnalyzerLoadFailureEventArgs.FailureErrorCode.NoAnalyzers:
                    id = Choose(language, WRN_NoAnalyzerInAssemblyId, WRN_NoAnalyzerInAssemblyIdCS, WRN_NoAnalyzerInAssemblyIdVB);
                    messageFormat = FeaturesResources.WRN_NoAnalyzerInAssembly;
                    message = string.Format(FeaturesResources.WRN_NoAnalyzerInAssembly, fullPath);
193
                    description = e.Exception.CreateDiagnosticDescription();
194 195 196 197 198 199
                    break;
                case AnalyzerLoadFailureEventArgs.FailureErrorCode.None:
                default:
                    id = string.Empty;
                    message = string.Empty;
                    messageFormat = string.Empty;
200
                    description = string.Empty;
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
                    return false;
            }

            return true;
        }

        private static string Choose(string language, string noLanguageMessage, string csharpMessage, string vbMessage)
        {
            if (language == null)
            {
                return noLanguageMessage;
            }

            return language == LanguageNames.CSharp ? csharpMessage : vbMessage;
        }
H
heejaechang 已提交
216 217
    }
}