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