using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; namespace LLVM.ClangTidy { /// /// 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. /// 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 Children_ = new Dictionary(); public CheckTree() : base(null, null) { } private CheckTree(string Name, CheckTree Parent) : base(Name, Parent) { } private void AddLeaf(string Name, DynamicPropertyDescriptor 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() .OfType>() .Where(x => x.Attributes.OfType().Count() > 0) .Select(x => new KeyValuePair, string>( x, x.Attributes.OfType().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 Children { get { return Children_; } } } public class CheckLeaf : CheckTreeNode { private DynamicPropertyDescriptor Property_; public CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor 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; } } } }