lebedev.ri added inline comments.

================
Comment at: clang-tidy/readability/FunctionSizeCheck.cpp:31
+        !(isa<ParmVarDecl>(VD) || isa<DecompositionDecl>(VD)) &&
+        !VD->getLocation().isMacroID())
+      Info.Variables++;
----------------
aaron.ballman wrote:
> This isn't the restriction I was envisioning. I was thinking more along the 
> lines of:
> ```
> bool VisitStmtExpr(StmtExpr *SE) override {
>   ++StructNesting;
>   Base::VisitStmtExpr(SE);
>   --StructNesting;
>   return true;
> }
> ```
> Basically -- treat a statement expression the same as an inner struct or 
> lambda because the variables declared within it are scoped *only* to the 
> statement expression.
> 
> You could argue that we should also add: `if (!SE->getLocation.isMacroID()) { 
> Base::VisitStmtExpr(SE); }` to the top of the function so that you only treat 
> statement expressions that are themselves in a macro expansion get this 
> special treatment. e.g.,
> ```
> void one_var() {
>   (void)({int a = 12; a;});
> }
> 
> #define M ({int a = 12; a;})
> void zero_var() {
>   (void)M;
> }
> ```
> I don't have strong opinions on this, but weakly lean towards treating all 
> statement expressions the same way.
Ah, i did not really knew about `GNU Statement Expression`, so i did not ignore 
them.
Test added, and ignored.


================
Comment at: test/clang-tidy/readability-function-size.cpp:207-212
+void variables_8() {
+  int a, b;
+  struct A {
+    A(int c, int d);
+  };
+}
----------------
aaron.ballman wrote:
> JonasToth wrote:
> > lebedev.ri wrote:
> > > aaron.ballman wrote:
> > > > lebedev.ri wrote:
> > > > > aaron.ballman wrote:
> > > > > > JonasToth wrote:
> > > > > > > lebedev.ri wrote:
> > > > > > > > aaron.ballman wrote:
> > > > > > > > > lebedev.ri wrote:
> > > > > > > > > > aaron.ballman wrote:
> > > > > > > > > > > lebedev.ri wrote:
> > > > > > > > > > > > aaron.ballman wrote:
> > > > > > > > > > > > > lebedev.ri wrote:
> > > > > > > > > > > > > > aaron.ballman wrote:
> > > > > > > > > > > > > > > I think the current behavior here is correct and 
> > > > > > > > > > > > > > > the previous behavior was incorrect. However, it 
> > > > > > > > > > > > > > > brings up an interesting question about what to 
> > > > > > > > > > > > > > > do here:
> > > > > > > > > > > > > > > ```
> > > > > > > > > > > > > > > void f() {
> > > > > > > > > > > > > > >   struct S {
> > > > > > > > > > > > > > >     void bar() {
> > > > > > > > > > > > > > >       int a, b;
> > > > > > > > > > > > > > >     }
> > > > > > > > > > > > > > >   };
> > > > > > > > > > > > > > > }
> > > > > > > > > > > > > > > ```
> > > > > > > > > > > > > > > Does `f()` contain zero variables or two? I would 
> > > > > > > > > > > > > > > contend that it has no variables because S::bar() 
> > > > > > > > > > > > > > > is a different scope than f(). But I can see a 
> > > > > > > > > > > > > > > case being made about the complexity of f() being 
> > > > > > > > > > > > > > > increased by the presence of the local class 
> > > > > > > > > > > > > > > definition. Perhaps this is a different facet of 
> > > > > > > > > > > > > > > the test about number of types?
> > > > > > > > > > > > > > As previously briefly discussed in IRC, i 
> > > > > > > > > > > > > > **strongly** believe that the current behavior is 
> > > > > > > > > > > > > > correct, and `readability-function-size`
> > > > > > > > > > > > > > should analyze/diagnose the function as a whole, 
> > > > > > > > > > > > > > including all sub-classes/sub-functions.
> > > > > > > > > > > > > Do you know of any coding standards related to this 
> > > > > > > > > > > > > check that weigh in on this?
> > > > > > > > > > > > > 
> > > > > > > > > > > > > What do you think about this:
> > > > > > > > > > > > > ```
> > > > > > > > > > > > > #define SWAP(x, y) ({__typeof__(x) temp = x; x = y; y 
> > > > > > > > > > > > > = x;})
> > > > > > > > > > > > > 
> > > > > > > > > > > > > void f() {
> > > > > > > > > > > > >   int a = 10, b = 12;
> > > > > > > > > > > > >   SWAP(a, b);
> > > > > > > > > > > > > }
> > > > > > > > > > > > > ```
> > > > > > > > > > > > > Does f() have two variables or three? Should presence 
> > > > > > > > > > > > > of the `SWAP` macro cause this code to be more 
> > > > > > > > > > > > > complex due to having too many variables?
> > > > > > > > > > > > Datapoint: the doc 
> > > > > > > > > > > > (`docs/clang-tidy/checks/readability-function-size.rst`)
> > > > > > > > > > > >  actually already states that macros *are* counted.
> > > > > > > > > > > > 
> > > > > > > > > > > > ```
> > > > > > > > > > > > .. option:: StatementThreshold
> > > > > > > > > > > > 
> > > > > > > > > > > >    Flag functions exceeding this number of statements. 
> > > > > > > > > > > > This may differ
> > > > > > > > > > > >    significantly from the number of lines for 
> > > > > > > > > > > > macro-heavy code. The default is
> > > > > > > > > > > >    `800`.
> > > > > > > > > > > > ```
> > > > > > > > > > > > ```
> > > > > > > > > > > > .. option:: NestingThreshold
> > > > > > > > > > > > 
> > > > > > > > > > > >     Flag compound statements which create next nesting 
> > > > > > > > > > > > level after
> > > > > > > > > > > >     `NestingThreshold`. This may differ significantly 
> > > > > > > > > > > > from the expected value
> > > > > > > > > > > >     for macro-heavy code. The default is `-1` (ignore 
> > > > > > > > > > > > the nesting level).
> > > > > > > > > > > > ```
> > > > > > > > > > > My concerns relate to what's considered a "variable 
> > > > > > > > > > > declared in the body" (per the documentation) in relation 
> > > > > > > > > > > to function complexity. To me, if the variable is not 
> > > > > > > > > > > accessible lexically within the body of the function, 
> > > > > > > > > > > it's not adding to the function's complexity *for local 
> > > > > > > > > > > variables*. It may certainly be adding other complexity, 
> > > > > > > > > > > of course.
> > > > > > > > > > > 
> > > > > > > > > > > I would have a very hard time explaining to a user that 
> > > > > > > > > > > variables they cannot see or change (assuming the macro 
> > > > > > > > > > > is in a header file out of their control) contribute to 
> > > > > > > > > > > their function's complexity. Similarly, I would have 
> > > > > > > > > > > difficulty explaining that variables in an locally 
> > > > > > > > > > > declared class member function contribute to the number 
> > > > > > > > > > > of variables in the outer function body, but the class 
> > > > > > > > > > > data members somehow do not.
> > > > > > > > > > > 
> > > > > > > > > > > (per the documentation) 
> > > > > > > > > > 
> > > > > > > > > > Please note that the word `complexity` is not used in the 
> > > > > > > > > > **documentation**, only `size` is.
> > > > > > > > > > 
> > > > > > > > > > There also is the other side of the coin:
> > > > > > > > > > 
> > > > > > > > > > ```
> > > > > > > > > > #define simple_macro_please_ignore \
> > > > > > > > > >   the; \
> > > > > > > > > >   actual; \
> > > > > > > > > >   content; \
> > > > > > > > > >   of; \
> > > > > > > > > >   the; \
> > > > > > > > > >   foo();
> > > > > > > > > > 
> > > > > > > > > > // Very simple function, nothing to see.
> > > > > > > > > > void foo() {
> > > > > > > > > >   simple_macro_please_ignore();
> > > > > > > > > > }
> > > > > > > > > > 
> > > > > > > > > > #undef simple_macro_please_ignore
> > > > > > > > > > ```
> > > > > > > > > > 
> > > > > > > > > > In other words, if we ignore macros, it would be possible 
> > > > > > > > > > to abuse them to artificially reduce complexity, by hiding 
> > > > > > > > > > it in the macros.
> > > > > > > > > > I agree that it's total abuse of macros, but macros are in 
> > > > > > > > > > general not nice, and it would not be good to give such 
> > > > > > > > > > things a pass.
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > > My concerns relate to what's considered a "variable 
> > > > > > > > > > > declared in the body" (per the documentation) in relation 
> > > > > > > > > > > to function complexity.
> > > > > > > > > > 
> > > > > > > > > > Could you please clarify, at this point, your concerns are 
> > > > > > > > > > only about this new part of the check (variables), or for 
> > > > > > > > > > the entire check?
> > > > > > > > > > In other words, if we ignore macros, it would be possible 
> > > > > > > > > > to abuse them to artificially reduce complexity, by hiding 
> > > > > > > > > > it in the macros.
> > > > > > > > > 
> > > > > > > > > I don't disagree, that's why I'm trying to explore the 
> > > > > > > > > boundaries. Your example does artificially reduce complexity. 
> > > > > > > > > My example using swap does not -- it's an idiomatic swap 
> > > > > > > > > macro where the inner variable declaration adds no complexity 
> > > > > > > > > to the calling function as it's not exposed to the calling 
> > > > > > > > > function.
> > > > > > > > > 
> > > > > > > > > > Could you please clarify, at this point, your concerns are 
> > > > > > > > > > only about this new part of the check (variables), or for 
> > > > > > > > > > the entire check?
> > > > > > > > > 
> > > > > > > > > Only the new part of the check involving variables.
> > > > > > > > > > Could you please clarify, at this point, your concerns are 
> > > > > > > > > > only about this new part of the check (variables), or for 
> > > > > > > > > > the entire check?
> > > > > > > > 
> > > > > > > > > Only the new part of the check involving variables.
> > > > > > > > 
> > > > > > > > OK.
> > > > > > > > 
> > > > > > > > This should be split into two boundaries:
> > > > > > > > * macros
> > > > > > > > * the nested functions/classes/methods in classes.
> > > > > > > > 
> > > > > > > > I *think* it may make sense to give the latter a pass, no 
> > > > > > > > strong opinion here.
> > > > > > > > But not macros.
> > > > > > > > (Also, i think it would be good to treat macros consistently 
> > > > > > > > within the check.)
> > > > > > > > 
> > > > > > > > Does anyone else has an opinion on how that should be handled?
> > > > > > > what is the current behaviour for aarons nested function?
> > > > > > > i checked cppcoreguidelines and hicpp and they did not mention 
> > > > > > > such a case and i do not recall any rule that might relate to it.
> > > > > > > 
> > > > > > > I think aaron has a good point with:
> > > > > > > > I would have a very hard time explaining to a user that 
> > > > > > > > variables they cannot see or change (assuming the macro is in a 
> > > > > > > > header file out of their control) contribute to their 
> > > > > > > > function's complexity. Similarly, I would have difficulty 
> > > > > > > > explaining that variables in an locally declared class member 
> > > > > > > > function contribute to the number of variables in the outer 
> > > > > > > > function body, but the class data members somehow do not.
> > > > > > > 
> > > > > > > But I see no way to distinguish between "good" and "bad" macros, 
> > > > > > > so macro expansions should add to the variable count, even though 
> > > > > > > your swap macro is a valid counter example.
> > > > > > > But I see no way to distinguish between "good" and "bad" macros, 
> > > > > > > so macro expansions should add to the variable count, even though 
> > > > > > > your swap macro is a valid counter example.
> > > > > > 
> > > > > > I would constrain it this way: variables declared in local class 
> > > > > > member function definitions and expression statements within a 
> > > > > > macro expansion do not contribute to the variable count, all other 
> > > > > > local variables do. e.g.,
> > > > > > ```
> > > > > > #define SWAP(x, y) ({__typeof__(x) temp = x; x = y; y = x;})
> > > > > > 
> > > > > > void two_variables() {
> > > > > >   int a = 10, b = 12;
> > > > > >   SWAP(a, b);
> > > > > > }
> > > > > > 
> > > > > > void three_variables() {
> > > > > >   int a = 10, b = 12;
> > > > > >   ({__typeof__(x) temp = x; x = y; y = x;})
> > > > > > }
> > > > > > 
> > > > > > void one_variable() {
> > > > > >   int i = 12;
> > > > > >   class C {
> > > > > >     void four_variables() {
> > > > > >       int a, b, c, d;
> > > > > >     }
> > > > > >   };
> > > > > > }
> > > > > > 
> > > > > > #define FOO(x) (x + ({int i = 12; i;}))
> > > > > > 
> > > > > > void five_variables() {
> > > > > >   int a, b, c, d = FOO(100);
> > > > > >   float f;
> > > > > > }
> > > > > > ```
> > > > > > I would constrain it this way: variables declared in local class 
> > > > > > member function definitions and expression statements within a 
> > > > > > macro expansion do not contribute to the variable count, all other 
> > > > > > local variables do.
> > > > > 
> > > > > But we do already count statements, branches and compound statements 
> > > > > in all those cases in this check.
> > > > > Why should variables be an exception?
> > > > > But we do already count statements, branches and compound statements 
> > > > > in all those cases in this check.
> > > > Why should variables be an exception?
> > > > 
> > > > Why should variables that are entirely inaccessible to the function 
> > > > count towards the function's variable complexity?
> > > > 
> > > > Things like macros count towards a function's line count because the 
> > > > macros are expanded into the function. I don't agree with this choice, 
> > > > but I can at least explain it to someone I'm teaching. In the case of 
> > > > variable declarations, I have no justification for those variables 
> > > > adding complexity because they cannot be named within the function even 
> > > > though the macro is expanded in the function. Yet the check doesn't 
> > > > count global variables which do add to function complexity when used 
> > > > within the function.
> > > > 
> > > > For those design reasons, I'd also be opposed to diagnosing this 
> > > > (assume it requires 2 variables to trigger the diagnostic):
> > > > ```
> > > > void one_variable() {
> > > >   auto lambda = []() { int a = 12, b = 100; return a + b; };
> > > > }
> > > > ```
> > > > which is functionally equivalent to:
> > > > ```
> > > > void one_variable() {
> > > >   struct S {
> > > >     int operator()() { int a = 12, b = 100; return a + b; }
> > > >   } lambda;
> > > > }
> > > > ```
> > > Ok, done. But this raises another question:
> > > ```
> > > #define vardecl(type, name) type name;
> > > void variables_15() {
> > >   // FIXME: surely we should still warn here?
> > >   vardecl(int, a);
> > >   vardecl(int, b);
> > > }
> > > ```
> > > I'm guessing we want to still warn in cases like this? 
> > how would you differentiate? I am against trying to get all macro cases 
> > right, either warn for everything in macros or nothing.
> > I'm guessing we want to still warn in cases like this?
> 
> That would be nice, yes. That's why the cut-point I was recommending were 
> situations where the declared variables are not accessible within the 
> function.
Ok. Any hint on how to actually do that?
I guess i could look at `DeclRefExpr`, but that would only count those macro 
variables,
that are actually used, while the check is trying not to differentiate.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D44602



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to