| File: RestoreCommand\Logging\WarningPropertiesCollection.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Commands\NuGet.Commands.csproj (NuGet.Commands) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #nullable disable using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using NuGet.Common; using NuGet.ProjectModel; using NuGet.Shared; namespace NuGet.Commands { /// <summary> /// Class to hold ProjectWide and PackageSpecific WarningProperties. /// </summary> internal class WarningPropertiesCollection : IEquatable<WarningPropertiesCollection> { private readonly ConcurrentDictionary<string, string> _getTargetGraphAliasCache = new ConcurrentDictionary<string, string>(); /// <summary> /// Contains the target frameworks for the project. /// These are used for no warn filtering in case of a log message without a target graph. /// </summary> public IReadOnlyList<string> ProjectFrameworks { get; } /// <summary> /// Contains Project wide properties for Warnings. /// </summary> public WarningProperties ProjectWideWarningProperties { get; } /// <summary> /// Contains Package specific properties for Warnings. /// NuGetLogCode -> LibraryId -> Set of Frameworks. /// </summary> public PackageSpecificWarningProperties PackageSpecificWarningProperties { get; } public WarningPropertiesCollection(WarningProperties projectWideWarningProperties, PackageSpecificWarningProperties packageSpecificWarningProperties, IReadOnlyList<string> projectFrameworks) { ProjectWideWarningProperties = projectWideWarningProperties; PackageSpecificWarningProperties = packageSpecificWarningProperties; ProjectFrameworks = projectFrameworks ?? new ReadOnlyCollection<string>(new List<string>()); } /// <summary> /// Attempts to suppress a warning log message or upgrade it to error log message. /// The decision is made based on the Package Specific or Project wide warning properties. /// </summary> /// <param name="message">Message that should be suppressed or upgraded to an error.</param> /// <returns>Bool indicating is the warning should be suppressed or not. /// If not then the param message should have been mutated to an error</returns> public bool ApplyWarningProperties(IRestoreLogMessage message) { if (ApplyProjectWideNoWarnProperties(message, ProjectWideWarningProperties) || ApplyPackageSpecificNoWarnProperties(message)) { return true; } else { ApplyWarningAsErrorProperties(message); return false; } } /// <summary> /// Attempts to suppress a warning log message. /// The decision is made based on the Package Specific or Project wide no warn properties. /// </summary> /// <param name="message">Message that should be suppressed.</param> /// <returns>Bool indicating is the warning should be suppressed or not.</returns> public bool ApplyNoWarnProperties(IRestoreLogMessage message) { return ApplyProjectWideNoWarnProperties(message, ProjectWideWarningProperties) || ApplyPackageSpecificNoWarnProperties(message); } /// <summary> /// Method is used to upgrade a warning to an error if needed. /// </summary> /// <param name="message">Message which should be upgraded to error if needed.</param> public void ApplyWarningAsErrorProperties(IRestoreLogMessage message) { ApplyProjectWideWarningsAsErrorProperties(message, ProjectWideWarningProperties); } /// <summary> /// Method is used to check is a warning should be suppressed due to package specific no warn properties. /// </summary> /// <param name="message">Message to be checked for no warn.</param> /// <returns>bool indicating if the IRestoreLogMessage should be suppressed or not.</returns> private bool ApplyPackageSpecificNoWarnProperties(IRestoreLogMessage message) { if (message.Level == LogLevel.Warning && PackageSpecificWarningProperties != null && !string.IsNullOrEmpty(message.LibraryId)) { // If the message does not contain a target graph, assume that it is applicable for all project frameworks. if (!message.TargetGraphs.Any()) { // Suppress the warning if the code + libraryId combination is suppressed for all project frameworks. if (ProjectFrameworks.Count > 0 && ProjectFrameworks.All(e => PackageSpecificWarningProperties.Contains(message.Code, message.LibraryId, e))) { return true; } } else { bool legacy = ProjectFrameworks.Count == 1 && string.IsNullOrEmpty(ProjectFrameworks[0]); // Get all the target graphs for which code + libraryId combination is not suppressed. message.TargetGraphs = message .TargetGraphs .Where(e => !PackageSpecificWarningProperties.Contains(message.Code, message.LibraryId, legacy ? string.Empty : GetTargetGraphAlias(e))) .ToList(); // If the message is left with no target graphs then suppress it. if (message.TargetGraphs.Count == 0) { return true; } } } // The message is not a warning or it does not contain a LibraryId or it is not suppressed in package specific settings. return false; } /// <summary> /// Method is used to check is a warning should be suppressed due to project wide no warn properties. /// </summary> /// <param name="message">Message to be checked for no warn.</param> /// <returns>bool indicating if the ILogMessage should be suppressed or not.</returns> public static bool ApplyProjectWideNoWarnProperties(ILogMessage message, WarningProperties warningProperties) { if (message.Level == LogLevel.Warning && warningProperties != null) { if (warningProperties.NoWarn.Contains(message.Code)) { // If the project wide NoWarn contains the message code then suppress it. return true; } } // the project wide NoWarn does contain the message code. do not suppress the warning. return false; } /// <summary> /// Method is used to check is a warning should be treated as an error. /// </summary> /// <param name="message">Message which should be upgraded to error if needed.</param> public static void ApplyProjectWideWarningsAsErrorProperties(ILogMessage message, WarningProperties warningProperties) { if (message is null) { throw new ArgumentNullException(nameof(message)); } if (message.Level == LogLevel.Warning && warningProperties != null) { if ((warningProperties.AllWarningsAsErrors && message.Code > NuGetLogCode.Undefined && !warningProperties.WarningsNotAsErrors.Contains(message.Code)) || warningProperties.WarningsAsErrors.Contains(message.Code)) { // If the project wide AllWarningsAsErrors is true, the message has a valid code and warning not as error is not enabled or // Project wide WarningsAsErrors contains the message code then upgrade to error. message.Message = string.Format(CultureInfo.CurrentCulture, Strings.WarningAsError, message.Message); message.Level = LogLevel.Error; } } } private string GetTargetGraphAlias(string targetGraph) { return _getTargetGraphAliasCache.GetOrAdd(targetGraph, (s) => s.Split('/')[0]); } public override int GetHashCode() { var hashCode = new HashCodeCombiner(); hashCode.AddObject(ProjectWideWarningProperties); hashCode.AddObject(PackageSpecificWarningProperties); hashCode.AddSequence(ProjectFrameworks); return hashCode.CombinedHash; } public override bool Equals(object obj) { return Equals(obj as WarningPropertiesCollection); } public bool Equals(WarningPropertiesCollection other) { if (other == null) { return false; } if (ReferenceEquals(this, other)) { return true; } return EqualityUtility.EqualsWithNullCheck(ProjectWideWarningProperties, other.ProjectWideWarningProperties) && EqualityUtility.EqualsWithNullCheck(PackageSpecificWarningProperties, other.PackageSpecificWarningProperties) && EqualityUtility.OrderedEquals(ProjectFrameworks, other.ProjectFrameworks, (fx) => fx, orderComparer: StringComparer.OrdinalIgnoreCase, StringComparer.OrdinalIgnoreCase); } } }