Add compilation warnings, in addition to using the above Develop clang plug-in: 0 basic feeling underlying group The mentioned clang plug-in,
You can also develop clang directly
1. Develop clang and use ninja to ensure normal development speed
Compiling clang with Xcode is slow
ninja + Xcode can be used for development
Use Xcode for automatic code completion, code prompt, compilation check and function jump, which is convenient
1.1 installation ninja
Built with ninja
- Check whether it is installed
brew list | grep ^ninja
- To install
brew install ninja
1.2 download project
Download llvm project
git clone https://github.com/llvm/llvm-project
1.3 code generation and compilation
This step will be used repeatedly later. It is called job for short_ O
- Building llvm projects using Ninja
get into
cd /Users/jzd/Movies/A_B/llvm-projectX
Equivalent to
cd /yourPath/llvm-project
code generation
cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"
- Project compilation
cd /Users/jzd/Movies/A_B/llvm-projectX/build
ninja clang
effect:
➜ build git:(main) ninja clang
[3345/3345] Creating executable symlink bin/clang
Supplement: Xcode related, see above
1.4 the example in this article is to check the over nesting of if statements
Take a look at the effect and make a simple if judgment without reporting an error
Complex, just report an error
Problems to be solved
How is it complicated? At least 3 levels of different operators, nesting of calculations
Final effect
2. clang development, phase 1, identify the AST of if statement and simple warning processing
Add compilation warning for over nesting of if statements
A short paragraph, compilation principle
Compilation, preprocessing, syntax analysis, lexical analysis, semantic analysis, get AST
Get the complete abstract syntax tree and analyze if nodes. Is it too complex
-
First parse the code, parse
-
semantic analysis
2.1 locate the clang source code and log the if node in the AST
Navigate to semantic analysis file
/Users/jzd/Movies/A_B/llvm-projectX/clang/lib/Sema/SemaStmt.cpp
The method inside, IF statement
Add two lines of log code, code generation and compilation, that is, job_O
Add: how to locate it? You can see my notes in CSDN clang learning aid
StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, SourceLocation LParenLoc, Stmt *InitStmt, ConditionResult Cond, SourceLocation RParenLoc, Stmt *thenStmt, SourceLocation ElseLoc, Stmt *elseStmt) { //... // Add the following two sentences llvm:: dbgs() << "be in ActOnIfStmt, Found it if Conditional judgment \n"; CondExpr->dump(); return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc, thenStmt, ElseLoc, elseStmt); }
2.1.1 see the simple effect
- Above example
➜ build git:(main) ✗ cat /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp
The code is simple
void test(int a, int b){ if (a > 0 && b > 0){ } }
- Command, (this step is frequently debugged, hereinafter referred to as job_debug )
➜ build git:(main) ✗ /Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang -c /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp
AST to dump
be in ActOnIfStmt, Found it if Conditional judgment BinaryOperator 0x7fe8e9075ea0 '_Bool' '&&' |-BinaryOperator 0x7fe8e9075e08 '_Bool' '>' | |-ImplicitCastExpr 0x7fe8e9075df0 'int' <LValueToRValue> | | `-DeclRefExpr 0x7fe8e9075db0 'int' lvalue ParmVar 0x7fe8e9075b68 'a' 'int' | `-IntegerLiteral 0x7fe8e9075dd0 'int' 0 `-BinaryOperator 0x7fe8e9075e80 '_Bool' '>' |-ImplicitCastExpr 0x7fe8e9075e68 'int' <LValueToRValue> | `-DeclRefExpr 0x7fe8e9075e28 'int' lvalue ParmVar 0x7fe8e9075be8 'b' 'int' `-IntegerLiteral 0x7fe8e9075e48 'int' 0
2.2 from dump log to error warning
2.2.1 modification of warning source file
Enter the warning table of semantic analysis
/Users/jzd/Movies/A_B/llvm-projectX/clang/include/clang/Basic/DiagnosticSemaKinds.td
Add a warning line at the end of the file
// Add this line def warn_if_condition_too_complex: Warning<"if The statement is too complex. Fix it ...">; } // end of sema component.
2.2.2 continue to modify the semantic analysis file
/Users/jzd/Movies/A_B/llvm-projectX/clang/lib/Sema/SemaStmt.cpp
Auxiliary command
open /Users/jzd/Movies/A_B/llvm-projectX/clang/lib/Sema/SemaStmt.cpp
- Add method
using namespace clang; using namespace sema; // Add here // Two parameters are required, // if conditional AST // And semantic self, using Sema to report errors void DiagnoseIf(const Expr * If, Sema &S){ // Diag // The first parameter is the location where the warning is compiled // The second parameter, which compilation warning // << If->getSourceRange(); // Add source code range to produce highlight effect, S.Diag(If->getExprLoc(), diag:: warn_if_condition_too_complex) << If->getSourceRange(); }
- Call method
The semantic analysis method mentioned above
StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, SourceLocation LParenLoc, Stmt *InitStmt, ConditionResult Cond, SourceLocation RParenLoc, Stmt *thenStmt, SourceLocation ElseLoc, Stmt *elseStmt) { //... // Add call DiagnoseIf(Cond.Condition.get(), *this); return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc, thenStmt, ElseLoc, elseStmt); }
2.2.3 see the effect
Or the above example, simple cpp code
Generate code, recompile, job_O once
Use the compiled clang to debug, job_debug
The built warning is put into use
➜ build git:(main) ✗ /Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang -c /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:13:15: warning: if The statement is too complex. Fix it ... if (a > 0 && b > 0){ ~~~~~~^~~~~~~~ 1 warning generated.
Control of warning through diagnostic group
This is reflected in activating the compilation warning or invalidating the compilation warning through the flag of the command line
This time, I changed a file path
/Users/jzd/Movies/A_B/llvm-projectX/clang/include/clang/Basic/DiagnosticGroups.td
Add at the end of the text
def ComplexIf: DiagGroup<"complex-condition">;
Then change the warning table of semantic analysis above
/Users/jzd/Movies/A_B/llvm-projectX/clang/include/clang/Basic/DiagnosticSemaKinds.td
Modify the definition of warning
DefaultIgnore, which means to disable the warning by default
def warn_if_condition_too_complex: Warning<"if The statement is too complex. Fix it ...">, InGroup<ComplexIf>, DefaultIgnore;
Modify the code at the call. See the code location above
// Precise use, compile warning if(!Diags.isIgnored(diag:: warn_if_condition_too_complex, Cond.Condition.get()->getExprLoc())){ // This if judgment is used to optimize performance DiagnoseIf(Cond.Condition.get(), *this); }
Look at the effect
job_ After o,
job_debug is equivalent to
➜ build git:(main) ✗ /Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang -Wno-complex-condition -c /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp
The default option is to disable,
It takes time because the CPU calculates the compilation warning
-Wno complex condition. warning no is not required by default. This compilation group is complex condition
To activate the compilation warning for if check, use the option - wcomplex condition
(this step is a new debugging, hereinafter referred to as job_debug1 )
/Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang -Wcomplex-condition -c /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp
3. clang development, phase 2, warning processing of complex if statements
3.1, calculate the complexity of if nested statements
Back to our DSA
3.1.1 perceptual knowledge, AST expansion of simple code statements
-
Function declaration f1, corresponding declaration expression, declaration reference expression
-
An implicit cast expression is made for function f1
Got the function pointer of f1
-
Get the result of the function pointer call, f1(), call expression
-
Then there are various simple operations
3.1.2 if nested statement complexity
Focus on the binary operator node
If you encounter layer 3, you will report an error
3.1.3 modify code
- Calculation level, traversing the tree depth first
void DiagnoseIf(const Expr * IfRoot, Sema &S, const Expr * CurrentExpr, int CurrentNestingLevel){ // Ignore Imp pointer conversion of function CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); // dyn_cast dynamic transformation to see if it is a binary operator if (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ // Look at this binary operator, isn't it & & or|| if (BinaryOp->getOpcode() == BO_LAnd || BinaryOp->getOpcode() == BO_Or){ if (CurrentNestingLevel >= 2){ S.Diag(IfRoot->getExprLoc(), diag:: warn_if_condition_too_complex) << IfRoot->getSourceRange(); } else{ // For the tree, depth first traversal, there is a simple recursion DiagnoseIf(IfRoot, S, BinaryOp->getLHS(), CurrentNestingLevel + 1); DiagnoseIf(IfRoot, S, BinaryOp->getRHS(), CurrentNestingLevel + 1); } } } }
- Call part
DiagnoseIf(Cond.Condition.get(), *this, Cond.Condition.get(), 0);
3.1.5 verification
Compile job_O + debug job_debug1
- New use cases
void test(int a, int b, int c, int d, int e){ if (a > 0){ } // 0 if (a > 0 || b > 0){ } // 1 if ((a > 0 || b > 0) && c > 0){ } // 2 if (((a > 0 || b > 0) && c > 0) || d > 0){ } // 3 if ((((a > 0 || b > 0) && c > 0) || d > 0) && e > 0){ } // 4 if (((a > 0 || b > 0) && (c > 0 || d > 0)) || e > 0){ } // 5 }
- result
Example 0 ~ 4, normal
Example 5: the warning is repeated,
Repeat the warning and calculate the noise noisy
Simple algorithm error
Draw the corresponding tree and you'll see
➜ build git:(main) ✗ /Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang -Wcomplex-condition -c /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:18:37: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if (((a > 0 || b > 0) && c > 0) || d > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:20:48: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if ((((a > 0 || b > 0) && c > 0) || d > 0) && e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:22:48: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if (((a > 0 || b > 0) && (c > 0 || d > 0)) || e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:22:48: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if (((a > 0 || b > 0) && (c > 0 || d > 0)) || e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ 4 warnings generated.
3.2. Solve bug 1. Repeat warning
Cause, as shown in the figure
Solve the problem. Report an error if you encounter one. End
Code modification
bool DiagnoseIf(const Expr * IfRoot, Sema &S, const Expr * CurrentExpr, int CurrentNestingLevel){ // Ignore Imp pointer conversion of function CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); // dyn_cast dynamic transformation to see if it is a binary operator if (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ // Look at this binary operator, isn't it & & or|| if (BinaryOp->getOpcode() == BO_LAnd || BinaryOp->getOpcode() == BO_LOr){ if (CurrentNestingLevel >= 2){ S.Diag(IfRoot->getExprLoc(), diag:: warn_if_condition_too_complex) << IfRoot->getSourceRange(); return false; } else{ // For the tree, depth first traversal, there is a simple recursion if (DiagnoseIf(IfRoot, S, BinaryOp->getLHS(), CurrentNestingLevel + 1)){ if (DiagnoseIf(IfRoot, S, BinaryOp->getRHS(), CurrentNestingLevel + 1)){ return true; } else{ return false; } } else{ return false; } } } } return true; }
After modification, see the effect
Go through the process,
Two job s go through it
Normal (or just the use case)
➜ build git:(main) ✗ /Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang -Wcomplex-condition -c /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:18:37: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if (((a > 0 || b > 0) && c > 0) || d > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:20:48: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if ((((a > 0 || b > 0) && c > 0) || d > 0) && e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ /Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:22:48: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if (((a > 0 || b > 0) && (c > 0 || d > 0)) || e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ 3 warnings generated.
4. clang development, stage 3, continuously debug and encounter new situations
4.1. The construction mode of AST shall be considered
4.1.1 new use cases
Simple repetition, it seems, without nesting
if (a > 0 || b > 0 || c > 0 || d > 0 || e > 0){ }
4.1.2 run (the process is the same as above)
/Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:24:42: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if (a > 0 || b > 0 || c > 0 || d > 0 || e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
4.1.3 analysis of problems
C + + allows continuous judgment to be written without parentheses
In fact, the use case has an equivalence for semantic analysis
In this way, the binary operator judgment of AST is nested three layers
This situation needs to be avoided
4.1.4 solution
Auxiliary method
// appointment // Nothing, 0 // && , 1 // || , 2 int valOfExpresion(const Expr * BOp){ BOp = BOp->IgnoreParenImpCasts(); if (const auto * BinaryOp = dyn_cast<BinaryOperator>(BOp)){ if (BinaryOp->getOpcode() == BO_LAnd){ return 1; } else if (BinaryOp->getOpcode() == BO_LOr){ return 2; } } return 0; }
Modification method,
Add judgment if the parent binary operator node and the child binary operator node are equal
This layer count, ignore
bool DiagnoseIf(const Expr * IfRoot, Sema &S, const Expr * CurrentExpr, int CurrentNestingLevel){ // Ignore Imp pointer conversion of function CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); // dyn_cast dynamic transformation to see if it is a binary operator if (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ // Look at this binary operator, isn't it & & or|| if (BinaryOp->getOpcode() == BO_LAnd || BinaryOp->getOpcode() == BO_LOr){ // For the tree, depth first traversal, there is a simple recursion int val = valOfExpresion(CurrentExpr); int valLhs = valOfExpresion(BinaryOp->getLHS()); // levelLhs, consider whether the access of this layer should be included in the calculation or ignored int levelLhs = 1; if (val == valLhs){ levelLhs = 0; } int valRhs = valOfExpresion(BinaryOp->getRHS()); int levelRhs = 1; if (val == valRhs){ levelRhs = 0; } llvm:: dbgs() << "levelRhs: " << levelRhs << " \n" << "levelLhs: " << levelLhs<< " \n\n\n"; if (CurrentNestingLevel >= 2){ S.Diag(IfRoot->getExprLoc(), diag:: warn_if_condition_too_complex) << IfRoot->getSourceRange(); return false; } else if (DiagnoseIf(IfRoot, S, BinaryOp->getLHS(), CurrentNestingLevel + levelLhs)){ if (DiagnoseIf(IfRoot, S, BinaryOp->getRHS(), CurrentNestingLevel + levelRhs)){ return true; } else{ return false; } } else{ return false; } } } return true; }
4.1.5 error demonstration
Return to front
If you nest two layers in this way, you will report an error here
Judge as |, & &, and then decide to return,
An error is reported only when this layer is considered, + 1 layer and 3 layers are nested
The value of levelLhs considers whether the access of this layer should be included in the calculation or ignored
The same is true for the value of levelRhs
bool DiagnoseIf(const Expr * IfRoot, Sema &S, const Expr * CurrentExpr, int CurrentNestingLevel){ if (CurrentNestingLevel >= 2){ S.Diag(IfRoot->getExprLoc(), diag:: warn_if_condition_too_complex) << IfRoot->getSourceRange(); return false; } // Ignore Imp pointer conversion of function CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); // dyn_cast dynamic transformation to see if it is a binary operator if (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ // Look at this binary operator, isn't it & & or|| if (BinaryOp->getOpcode() == BO_LAnd || BinaryOp->getOpcode() == BO_LOr){ // For the tree, depth first traversal, there is a simple recursion int val = valOfExpresion(CurrentExpr); int valLhs = valOfExpresion(BinaryOp->getLHS()); // levelLhs, consider whether the access of this layer should be included in the calculation or ignored int levelLhs = 1; if (val == valLhs){ levelLhs = 0; } int valRhs = valOfExpresion(BinaryOp->getRHS()); int levelRhs = 1; if (val == valRhs){ levelRhs = 0; } if (DiagnoseIf(IfRoot, S, BinaryOp->getLHS(), CurrentNestingLevel + levelLhs)){ if (DiagnoseIf(IfRoot, S, BinaryOp->getRHS(), CurrentNestingLevel + levelRhs)){ return true; } else{ return false; } } else{ return false; } } } return true; }
Error demonstration effect
/Users/jzd/Movies/A_a/Clang/proj/collect/999998/999998/test.cpp:16:26: warning: if The statement is too complex. Fix it ... [-Wcomplex-condition] if ((a > 0 || b > 0) && c > 0){ } ~~~~~~~~~~~~~~~~~^~~~~~~~
4.2 other situations
4.2.1 in the if statement, there is macro expansion
Macro expansion is during preprocessing,
When we deal with AST, we need to avoid it in the semantic analysis stage
Solution, omitted
4.2.2 template function of C + +, repeated error reporting
Solution, omitted
Add clang to Xcode
The operation is relatively simple
- Customize two user settings
CC and CXX
- Fill in content
The path of CC is
/Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang
The path to CXX is
/Users/jzd/Movies/A_B/llvm-projectX/build/bin/clang++
- Set file compilation options
Want this warning
-Wcomplex-condition