1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Linq; 5 using System.Text; 6 7 namespace LLVM.ClangTidy 8 { 9 /// <summary> 10 /// CheckTree is used to group checks into categories and subcategories. For 11 /// example, given the following list of checks: 12 /// 13 /// llvm-include-order 14 /// llvm-namespace-comment 15 /// llvm-twine-local 16 /// llvm-header-guard 17 /// google-runtime-member-string-references 18 /// google-runtime-int 19 /// google-readability-namespace-comments 20 /// 21 /// the corresponding CheckTree would look like this: 22 /// 23 /// llvm 24 /// include-order 25 /// namespace-comment 26 /// twine-local 27 /// header-guard 28 /// google 29 /// runtime 30 /// member-string-references 31 /// int 32 /// readability 33 /// namespace-comments 34 /// redundant-smartptr-get 35 /// 36 /// This is useful when serializing a set of options out to a .clang-tidy file, 37 /// because we need to decide the most efficient way to serialize the sequence 38 /// of check commands, when to use wildcards, etc. For example, if everything 39 /// under google is inherited, we can simply leave that entry out entirely from 40 /// the .clang-tidy file. On the other hand, if anything is inherited, we *must 41 /// not* add or remove google-* by wildcard because that, by definition, means 42 /// the property is no longer inherited. When we can categorize the checks into 43 /// groups and subgroups like this, it is possible to efficiently serialize to 44 /// a minimal representative .clang-tidy file. 45 /// </summary> 46 47 public abstract class CheckTreeNode 48 { 49 private string Name_; 50 private CheckTreeNode Parent_; 51 CheckTreeNode(string Name, CheckTreeNode Parent)52 protected CheckTreeNode(string Name, CheckTreeNode Parent) 53 { 54 Name_ = Name; 55 Parent_ = Parent; 56 } 57 58 public string Path 59 { 60 get 61 { 62 if (Parent_ == null) 63 return null; 64 string ParentPath = Parent_.Path; 65 if (ParentPath == null) 66 return Name_; 67 return ParentPath + "-" + Name_; 68 } 69 } 70 71 public string Name 72 { 73 get 74 { 75 return Name_; 76 } 77 } 78 79 80 public abstract int CountChecks { get; } 81 public abstract int CountExplicitlyDisabledChecks { get; } 82 public abstract int CountExplicitlyEnabledChecks { get; } 83 public abstract int CountInheritedChecks { get; } 84 } 85 86 public class CheckTree : CheckTreeNode 87 { 88 private Dictionary<string, CheckTreeNode> Children_ = new Dictionary<string, CheckTreeNode>(); CheckTree()89 public CheckTree() 90 : base(null, null) 91 { 92 93 } 94 CheckTree(string Name, CheckTree Parent)95 private CheckTree(string Name, CheckTree Parent) 96 : base(Name, Parent) 97 { 98 } 99 AddLeaf(string Name, DynamicPropertyDescriptor<bool> Property)100 private void AddLeaf(string Name, DynamicPropertyDescriptor<bool> Property) 101 { 102 Children_[Name] = new CheckLeaf(Name, this, Property); 103 } 104 AddOrCreateSubgroup(string Name)105 private CheckTree AddOrCreateSubgroup(string Name) 106 { 107 CheckTreeNode Subgroup = null; 108 if (Children_.TryGetValue(Name, out Subgroup)) 109 { 110 System.Diagnostics.Debug.Assert(Subgroup is CheckTree); 111 return (CheckTree)Subgroup; 112 } 113 114 CheckTree SG = new CheckTree(Name, this); 115 Children_[Name] = SG; 116 return SG; 117 } 118 Build(ClangTidyProperties Config)119 public static CheckTree Build(ClangTidyProperties Config) 120 { 121 // Since some check names contain dashes in them, it doesn't make sense to 122 // simply split all check names by dash and construct a huge tree. For 123 // example, in the check called google-runtime-member-string-references, 124 // we don't need each of those to be a different subgroup. So instead we 125 // explicitly specify the common breaking points at which a user might want 126 // to use a -* and everything else falls as a leaf under one of these 127 // categories. 128 // FIXME: This should be configurable without recompilation 129 CheckTree Root = new CheckTree(); 130 string[][] Groups = new string[][] { 131 new string[] {"boost"}, 132 new string[] {"cert"}, 133 new string[] {"clang", "diagnostic"}, 134 new string[] {"cppcoreguidelines", "interfaces"}, 135 new string[] {"cppcoreguidelines", "pro", "bounds"}, 136 new string[] {"cppcoreguidelines", "pro", "type"}, 137 new string[] {"google", "build"}, 138 new string[] {"google", "readability"}, 139 new string[] {"google", "runtime"}, 140 new string[] {"llvm"}, 141 new string[] {"misc"}, 142 }; 143 144 foreach (string[] Group in Groups) 145 { 146 CheckTree Subgroup = Root; 147 foreach (string Component in Group) 148 Subgroup = Subgroup.AddOrCreateSubgroup(Component); 149 } 150 151 var Props = Config.GetProperties() 152 .Cast<PropertyDescriptor>() 153 .OfType<DynamicPropertyDescriptor<bool>>() 154 .Where(x => x.Attributes.OfType<ClangTidyCheckAttribute>().Count() > 0) 155 .Select(x => new KeyValuePair<DynamicPropertyDescriptor<bool>, string>( 156 x, x.Attributes.OfType<ClangTidyCheckAttribute>().First().CheckName)); 157 var PropArray = Props.ToArray(); 158 foreach (var CheckInfo in PropArray) 159 { 160 string LeafName = null; 161 CheckTree Tree = Root.LocateCheckLeafGroup(CheckInfo.Value, out LeafName); 162 Tree.AddLeaf(LeafName, CheckInfo.Key); 163 } 164 return Root; 165 } 166 LocateCheckLeafGroup(string Check, out string LeafName)167 private CheckTree LocateCheckLeafGroup(string Check, out string LeafName) 168 { 169 string[] Components = Check.Split('-'); 170 string FirstComponent = Components.FirstOrDefault(); 171 if (FirstComponent == null) 172 { 173 LeafName = Check; 174 return this; 175 } 176 177 CheckTreeNode Subgroup = null; 178 if (!Children_.TryGetValue(FirstComponent, out Subgroup)) 179 { 180 LeafName = Check; 181 return this; 182 } 183 System.Diagnostics.Debug.Assert(Subgroup is CheckTree); 184 CheckTree Child = (CheckTree)Subgroup; 185 string ChildName = Check.Substring(FirstComponent.Length + 1); 186 return Child.LocateCheckLeafGroup(ChildName, out LeafName); 187 } 188 189 public override int CountChecks 190 { 191 get 192 { 193 return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountChecks; }); 194 } 195 } 196 197 public override int CountExplicitlyDisabledChecks 198 { 199 get 200 { 201 return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyDisabledChecks; }); 202 } 203 } 204 205 public override int CountExplicitlyEnabledChecks 206 { 207 get 208 { 209 return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyEnabledChecks; }); 210 } 211 } 212 public override int CountInheritedChecks 213 { 214 get 215 { 216 return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountInheritedChecks; }); 217 } 218 } 219 220 public IDictionary<string, CheckTreeNode> Children 221 { 222 get { return Children_; } 223 } 224 } 225 226 public class CheckLeaf : CheckTreeNode 227 { 228 private DynamicPropertyDescriptor<bool> Property_; 229 CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor<bool> Property)230 public CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor<bool> Property) 231 : base(Name, Parent) 232 { 233 Property_ = Property; 234 } 235 236 public override int CountChecks 237 { 238 get 239 { 240 return 1; 241 } 242 } 243 244 public override int CountExplicitlyDisabledChecks 245 { 246 get 247 { 248 if (Property_.IsInheriting) 249 return 0; 250 return (bool)Property_.GetValue(null) ? 0 : 1; 251 } 252 } 253 254 public override int CountExplicitlyEnabledChecks 255 { 256 get 257 { 258 if (Property_.IsInheriting) 259 return 0; 260 return (bool)Property_.GetValue(null) ? 1 : 0; 261 } 262 } 263 264 public override int CountInheritedChecks 265 { 266 get 267 { 268 return (Property_.IsInheriting) ? 1 : 0; 269 } 270 } 271 272 } 273 } 274