llvm-project/clang-tools-extra/clang-tidy-vs/ClangTidy/CheckTree.cs

274 lines
9.1 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace LLVM.ClangTidy
{
/// <summary>
/// CheckTree is used to group checks into categories and subcategories. For
/// example, given the following list of checks:
///
/// llvm-include-order
/// llvm-namespace-comment
/// llvm-twine-local
/// llvm-header-guard
/// google-runtime-member-string-references
/// google-runtime-int
/// google-readability-namespace-comments
///
/// the corresponding CheckTree would look like this:
///
/// llvm
/// include-order
/// namespace-comment
/// twine-local
/// header-guard
/// google
/// runtime
/// member-string-references
/// int
/// readability
/// namespace-comments
/// redundant-smartptr-get
///
/// This is useful when serializing a set of options out to a .clang-tidy file,
/// because we need to decide the most efficient way to serialize the sequence
/// of check commands, when to use wildcards, etc. For example, if everything
/// under google is inherited, we can simply leave that entry out entirely from
/// the .clang-tidy file. On the other hand, if anything is inherited, we *must
/// not* add or remove google-* by wildcard because that, by definition, means
/// the property is no longer inherited. When we can categorize the checks into
/// groups and subgroups like this, it is possible to efficiently serialize to
/// a minimal representative .clang-tidy file.
/// </summary>
public abstract class CheckTreeNode
{
private string Name_;
private CheckTreeNode Parent_;
protected CheckTreeNode(string Name, CheckTreeNode Parent)
{
Name_ = Name;
Parent_ = Parent;
}
public string Path
{
get
{
if (Parent_ == null)
return null;
string ParentPath = Parent_.Path;
if (ParentPath == null)
return Name_;
return ParentPath + "-" + Name_;
}
}
public string Name
{
get
{
return Name_;
}
}
public abstract int CountChecks { get; }
public abstract int CountExplicitlyDisabledChecks { get; }
public abstract int CountExplicitlyEnabledChecks { get; }
public abstract int CountInheritedChecks { get; }
}
public class CheckTree : CheckTreeNode
{
private Dictionary<string, CheckTreeNode> Children_ = new Dictionary<string, CheckTreeNode>();
public CheckTree()
: base(null, null)
{
}
private CheckTree(string Name, CheckTree Parent)
: base(Name, Parent)
{
}
private void AddLeaf(string Name, DynamicPropertyDescriptor<bool> Property)
{
Children_[Name] = new CheckLeaf(Name, this, Property);
}
private CheckTree AddOrCreateSubgroup(string Name)
{
CheckTreeNode Subgroup = null;
if (Children_.TryGetValue(Name, out Subgroup))
{
System.Diagnostics.Debug.Assert(Subgroup is CheckTree);
return (CheckTree)Subgroup;
}
CheckTree SG = new CheckTree(Name, this);
Children_[Name] = SG;
return SG;
}
public static CheckTree Build(ClangTidyProperties Config)
{
// Since some check names contain dashes in them, it doesn't make sense to
// simply split all check names by dash and construct a huge tree. For
// example, in the check called google-runtime-member-string-references,
// we don't need each of those to be a different subgroup. So instead we
// explicitly specify the common breaking points at which a user might want
// to use a -* and everything else falls as a leaf under one of these
// categories.
// FIXME: This should be configurable without recompilation
CheckTree Root = new CheckTree();
string[][] Groups = new string[][] {
new string[] {"boost"},
new string[] {"cert"},
new string[] {"clang", "diagnostic"},
new string[] {"cppcoreguidelines", "interfaces"},
new string[] {"cppcoreguidelines", "pro", "bounds"},
new string[] {"cppcoreguidelines", "pro", "type"},
new string[] {"google", "build"},
new string[] {"google", "readability"},
new string[] {"google", "runtime"},
new string[] {"llvm"},
new string[] {"misc"},
};
foreach (string[] Group in Groups)
{
CheckTree Subgroup = Root;
foreach (string Component in Group)
Subgroup = Subgroup.AddOrCreateSubgroup(Component);
}
var Props = Config.GetProperties()
.Cast<PropertyDescriptor>()
.OfType<DynamicPropertyDescriptor<bool>>()
.Where(x => x.Attributes.OfType<ClangTidyCheckAttribute>().Count() > 0)
.Select(x => new KeyValuePair<DynamicPropertyDescriptor<bool>, string>(
x, x.Attributes.OfType<ClangTidyCheckAttribute>().First().CheckName));
var PropArray = Props.ToArray();
foreach (var CheckInfo in PropArray)
{
string LeafName = null;
CheckTree Tree = Root.LocateCheckLeafGroup(CheckInfo.Value, out LeafName);
Tree.AddLeaf(LeafName, CheckInfo.Key);
}
return Root;
}
private CheckTree LocateCheckLeafGroup(string Check, out string LeafName)
{
string[] Components = Check.Split('-');
string FirstComponent = Components.FirstOrDefault();
if (FirstComponent == null)
{
LeafName = Check;
return this;
}
CheckTreeNode Subgroup = null;
if (!Children_.TryGetValue(FirstComponent, out Subgroup))
{
LeafName = Check;
return this;
}
System.Diagnostics.Debug.Assert(Subgroup is CheckTree);
CheckTree Child = (CheckTree)Subgroup;
string ChildName = Check.Substring(FirstComponent.Length + 1);
return Child.LocateCheckLeafGroup(ChildName, out LeafName);
}
public override int CountChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountChecks; });
}
}
public override int CountExplicitlyDisabledChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyDisabledChecks; });
}
}
public override int CountExplicitlyEnabledChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyEnabledChecks; });
}
}
public override int CountInheritedChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountInheritedChecks; });
}
}
public IDictionary<string, CheckTreeNode> Children
{
get { return Children_; }
}
}
public class CheckLeaf : CheckTreeNode
{
private DynamicPropertyDescriptor<bool> Property_;
public CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor<bool> Property)
: base(Name, Parent)
{
Property_ = Property;
}
public override int CountChecks
{
get
{
return 1;
}
}
public override int CountExplicitlyDisabledChecks
{
get
{
if (Property_.IsInheriting)
return 0;
return (bool)Property_.GetValue(null) ? 0 : 1;
}
}
public override int CountExplicitlyEnabledChecks
{
get
{
if (Property_.IsInheriting)
return 0;
return (bool)Property_.GetValue(null) ? 1 : 0;
}
}
public override int CountInheritedChecks
{
get
{
return (Property_.IsInheriting) ? 1 : 0;
}
}
}
}