| 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;
|
| }
|
| }
|
|
|
| }
|
| }
|