forked from OSchip/llvm-project
274 lines
9.1 KiB
C#
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;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|