Skip to content

Commit ad24fc6

Browse files
authored
Fix #12503 (usability: bogus suggestion in functionStatic message) (danmar#7978)
1 parent bb2bb4f commit ad24fc6

File tree

5 files changed

+248
-90
lines changed

5 files changed

+248
-90
lines changed

lib/checkclass.cpp

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,10 +2130,6 @@ void CheckClass::thisSubtractionError(const Token *tok)
21302130

21312131
void CheckClass::checkConst()
21322132
{
2133-
// This is an inconclusive check. False positives: #3322.
2134-
if (!mSettings->certainty.isEnabled(Certainty::inconclusive))
2135-
return;
2136-
21372133
if (!mSettings->severity.isEnabled(Severity::style) &&
21382134
!mSettings->isPremiumEnabled("functionConst") &&
21392135
!mSettings->isPremiumEnabled("functionStatic"))
@@ -2220,6 +2216,9 @@ void CheckClass::checkConst()
22202216
const bool suggestStatic = memberAccessed != MemberAccess::MEMBER && !func.isOperator();
22212217
if ((returnsPtrOrRef || func.isConst() || func.hasLvalRefQualifier()) && !suggestStatic)
22222218
continue;
2219+
if (!suggestStatic && !mSettings->certainty.isEnabled(Certainty::inconclusive))
2220+
// functionConst is inconclusive. False positives: #3322.
2221+
continue;
22232222
if (suggestStatic && func.isConst()) {
22242223
const auto overloads = func.getOverloadedFunctions();
22252224
if (overloads.size() > 1 && std::any_of(overloads.begin(), overloads.end(), [&](const Function* ovl) {
@@ -2717,16 +2716,10 @@ void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const st
27172716
}
27182717
else {
27192718
const std::string msg = foundAllBaseClasses ?
2720-
"Technically the member function '$symbol' can be static (but you may consider moving to unnamed namespace).\nThe member function '$symbol' can be made a static " :
2721-
"Either there is a missing 'override', or the member function '$symbol' can be static.\nUnless it overrides a base class member, the member function '$symbol' can be made a static ";
2722-
reportError(toks, Severity::performance, "functionStatic",
2723-
"$symbol:" + classname + "::" + funcname +"\n"
2724-
+ msg +
2725-
"function. Making a function static can bring a performance benefit since no 'this' instance is "
2726-
"passed to the function. This change should not cause compiler errors but it does not "
2727-
"necessarily make sense conceptually. Think about your design and the task of the function first - "
2728-
"is it a function that must not access members of class instances? And maybe it is more appropriate "
2729-
"to move this function to an unnamed namespace.", CWE398, Certainty::inconclusive);
2719+
"The member function '$symbol' can be static." :
2720+
"Either there is a missing 'override', or the member function '$symbol' can be static.";
2721+
reportError(toks, Severity::style, "functionStatic",
2722+
"$symbol:" + classname + "::" + funcname +"\n" + msg, CWE398, Certainty::normal);
27302723
}
27312724
}
27322725

man/checkers/functionConst.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# functionConst
2+
3+
**Message**: Technically the member function 'x' can be const<br/>
4+
**Category**: Robustness<br/>
5+
**Severity**: Style (Inconclusive)<br/>
6+
**Language**: C++
7+
8+
## Description
9+
10+
This checker identifies member functions that do not modify any member variables and therefore could be declared as `const`. A const member function promises not to modify the object's state and enables the function to be called on const objects.
11+
12+
The danger is that a const member function is allowed to have side effects outside the object. If you create a member function that formats the hard drive using
13+
system calls it might be technically possible to make the function const, but is it "correct"? Using a const object is not supposed to have side effects.
14+
15+
For methods that has no side effects whatsoever; making them const is recommended.
16+
17+
The checker analyzes member functions and detects when:
18+
- The function only reads member variables (never writes to them)
19+
- The function only calls other const member functions
20+
- The function only modifies parameters passed by reference or pointer (not member variables)
21+
- The function performs operations that don't change the object's logical state
22+
23+
This warning is marked as **inconclusive** because while the function can technically be made const, it may not always be "correct".
24+
25+
This warning helps improve code quality by:
26+
- Making the function's non-modifying nature explicit
27+
- Enabling the function to be called on const objects
28+
- Improving const-correctness throughout the codebase
29+
- Helping with compiler optimizations
30+
- Making code intentions clearer to other developers
31+
32+
## Motivation
33+
34+
The motivation of this checker is to improve robustness by making the code more const-correct.
35+
36+
## How to fix
37+
38+
Add the `const` keyword after the function signature to indicate that the function does not modify the object's state.
39+
40+
Before:
41+
```cpp
42+
class Rectangle {
43+
int width, height;
44+
public:
45+
int getWidth() { return width; }
46+
int getHeight() { return height; }
47+
int getArea() { return width * height; }
48+
49+
void printInfo() {
50+
std::cout << "Width: " << width << ", Height: " << height << std::endl;
51+
}
52+
53+
bool isSquare() {
54+
return width == height;
55+
}
56+
57+
void copyDataTo(Rectangle& other) {
58+
other.width = width;
59+
other.height = height;
60+
}
61+
};
62+
```
63+
64+
After:
65+
```cpp
66+
class Rectangle {
67+
int width, height;
68+
public:
69+
int getWidth() const { return width; }
70+
int getHeight() const { return height; }
71+
int getArea() const { return width * height; }
72+
73+
void printInfo() const {
74+
std::cout << "Width: " << width << ", Height: " << height << std::endl;
75+
}
76+
77+
bool isSquare() const {
78+
return width == height;
79+
}
80+
81+
void copyDataTo(Rectangle& other) const {
82+
other.width = width;
83+
other.height = height;
84+
}
85+
};
86+
```
87+
88+
## Related checkers
89+
90+
- `functionStatic` - for member functions that can be declared static
91+
- `constParameter` - for function parameters that can be const
92+
- `constParameterReference` - for reference parameters that can be const
93+
- `constParameterPointer` - for pointer parameters that can be const
94+
- `constVariable` - for local variables that can be const
95+
- `constVariableReference` - for local reference variables that can be const
96+
97+
## Notes
98+
99+
- This check is marked as **inconclusive** because the decision to make a function const should also consider the conceptual design
100+
- Virtual functions should be carefully considered before making them const, as this affects the entire inheritance hierarchy
101+
- Think about whether the function's purpose is to query state (should be const) or to perform an action (may not need to be const)

man/checkers/functionStatic.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# functionStatic
2+
3+
**Message**: The member function 'x' can be static<br/>
4+
**Category**: Readability<br/>
5+
**Severity**: Style<br/>
6+
**Language**: C++
7+
8+
## Description
9+
10+
This checker identifies member functions that do not access any non-static member variables or call any non-static member functions. Such functions can be declared as `static` to indicate that they don't require an object instance to operate.
11+
12+
This warning helps improve code quality by:
13+
- Making the function's independence from object state explicit
14+
- Enabling the function to be called without creating an object instance
15+
- Clarifying the function's scope and dependencies
16+
17+
## Motivation
18+
19+
The motivation of this checker is to improve readability.
20+
21+
## How to fix
22+
23+
Add the `static` keyword to the function declaration to indicate that it doesn't require an object instance.
24+
25+
Before:
26+
```cpp
27+
class Calculator {
28+
public:
29+
int add(int a, int b) {
30+
return a + b; // Only uses parameters
31+
}
32+
33+
void printMessage() {
34+
std::cout << "Hello World" << std::endl; // Uses no instance data
35+
}
36+
37+
bool isValidNumber(int num) {
38+
return num > 0 && num < 1000; // Pure function
39+
}
40+
};
41+
```
42+
43+
After:
44+
```cpp
45+
class Calculator {
46+
public:
47+
static int add(int a, int b) {
48+
return a + b; // Can be called as Calculator::add(5, 3)
49+
}
50+
51+
static void printMessage() {
52+
std::cout << "Hello World" << std::endl; // Can be called without instance
53+
}
54+
55+
static bool isValidNumber(int num) {
56+
return num > 0 && num < 1000; // Clearly indicates no state dependency
57+
}
58+
};
59+
```
60+
61+
## Related checkers
62+
63+
- `functionConst` - for member functions that can be declared const
64+

test/cli/proj-inline-suppress-unusedFunction/B.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ class B {
22
public:
33
B();
44

5-
void unusedFunctionTest();
5+
static void unusedFunctionTest();
66
};

0 commit comments

Comments
 (0)