Add a compile warning, in addition to using the clang plugin mentioned above: 0 base feel bottom group,

You can also develop Clang directly

1. Develop Clang and use Ninja to ensure normal development speed

Compiling Clang with Xcode is slow

It can be developed with Ninja + Xcode

Use Xcode code automatic completion, code prompts, compile check, function jump, convenient

1.1 installationninja

Build with NINJA
  • Check that it is installed

brew list | grep ^ninja

  • To install

brew install ninja

1.2 Download Project

Download the LLVM – project

git clone https://github.com/llvm/llvm-project

1.3 Code generation and compilation

This step, which will be used repeatedly later, is called job_O for short
  • Generate the LLVM project using Ninja

Enter the

cd /Users/jzd/Movies/A_B/llvm-project

Is equivalent to

cd /yourPath/llvm-project

Code generation

cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang; libcxx; libcxxabi"

  • Compile the project

cd /yourPath/llvm-project/build

ninja clang

Effect:

➜ build git:(main) ninja clang

[3345/3345] Creating executable symlink bin/clang

Addendum: Xcode related, see above

1.4 The example in this article is to check for excessive nesting of if statements

Look at the effect, simple if judgment, no error

If it is complex, an error is reported

Problems to be solved

What is complex? At least 3 layers of different operators, computation nesting

Final effect

2. Clang development, stage 1, AST identification of if statements, and simple warning processing

Add compilation warnings about excessive nesting of if statements
Let’s do a little bit of compilation

Compile, preprocess, parse, lexical, semantic, get AST

Get the whole abstract syntax tree, analyze if node, is too complex

  • Parse the code first

  • Semantic analysis, semantic analysis

2.1 Locate the CLang source code and log the IF node in the AST

Locate the semantic analysis file

/yourPath/llvm-project/clang/lib/Sema/SemaStmt.cpp

This method in here, IF statement

Add two lines of logging code, code generation and compilation, which is job_O

Supplement: How to locate it, can you see my notes in CSDN clang study 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 () << "in ActOnIfStmt, found if condition judgment \n"; CondExpr->dump(); return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc, thenStmt, ElseLoc, elseStmt); }Copy the code
2.1.1, simple effect
  • The example

➜ build git:(main) qualify cat/XXX /test.cpp

The code is very simple

void test(int a, int b){
    if (a > 0 && b > 0){
        
    }
}
Copy the code
  • Command, (this step is frequently debugged, hereinafter referred to as job_debug)

➜ build git:(main) qualify /yourPath/llvm-project/build/bin/ clang-c/XXX /test.cpp

The dump to AST

In the ActOnIfStmt, Found the if condition judgment BinaryOperator 0 x7fe8e9075ea0 '_Bool' '&&' | - BinaryOperator zero x7fe8e9075e08 '_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' 0Copy the code

2.2 From Dumping logs to Warning

2.2.1 Warning Source file modified

Enter the warning table for semantic analysis

/yourPath/llvm-project/clang/include/clang/Basic/DiagnosticSemaKinds.td

Add a warning line at the end of the file

Def warn_if_condition_too_complex: Warning<"if condition_too_complex" >; } // end of sema component.Copy the code
2.2.2 Continuing to Modify semantic Analysis Files

/yourPath/llvm-project/clang/lib/Sema/SemaStmt.cpp

Auxiliary command

open /yourPath/llvm-project/clang/lib/Sema/SemaStmt.cpp

  • Add methods
using namespace clang; using namespace sema; // Add // two arguments, Void DiagnoseIf(const Expr * if, sema&s){// if (const Expr * if, sema&s); // if (const Expr * if, sema&s); << If->getSourceRange(); << If->getSourceRange(); S.diag (If->getExprLoc(), diag:: warn_if_condition_too_complex) << If->getSourceRange(); }Copy the code
  • A method is called

Again, semantic analysis as mentioned above

StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, SourceLocation LParenLoc, Stmt *InitStmt, ConditionResult Cond, SourceLocation RParenLoc, Stmt *thenStmt, SourceLocation ElseLoc, Stmt *elseStmt) { //... DiagnoseIf(cond.condition.get (), *this); return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc, thenStmt, ElseLoc, elseStmt); }Copy the code
Then see the effect

Again, the example above, simple CPP code

Generate code, recompile, job_O

Debug using compiled clang, under job_debug

Create warning and put it into use

➜ build git:(main) Qualify /Users/ JZD /Movies/A_B/ llvm-projectx /build/bin/clang -c/XXX /test.cpp/XXX /test.cpp:13:15: Warning: If statement, too complex, fix it... if (a > 0 && b > 0){ ~~~~~~^~~~~~~~ 1 warning generated.Copy the code

Warnings are controlled through diagnostic group diagnostic Group

Activate or disable the compile warning by using flag on the command line

This time the file path is changed

/yourPath/llvm-project/clang/include/clang/Basic/DiagnosticGroups.td

Add at the end of the text

def ComplexIf: DiagGroup<"complex-condition">;
Copy the code

I’m going to change the semantic analysis warning form above

/yourPath/llvm-project/clang/include/clang/Basic/DiagnosticSemaKinds.td

Modify the definition of warnings

DefaultIgnore, which means, let the warning fail by default

Def warn_if_condition_too_complex: Warning<" condition" >, InGroup<ComplexIf>, DefaultIgnore;Copy the code

Modify the code at the invocation, as shown above

// Compile warning if(! Diags.isIgnored(diag:: Warn_if_condition_too_complex, cond.condition.get ()->getExprLoc())){ DiagnoseIf(cond.condition.get (), *this); }Copy the code
The effect to see

After job_O under,

Job_debug equivalent to the

➜ build git:(main) qualify /yourPath/llvm-project/build/bin/clang - wno-complex-condition-c/XXX /test.cpp

The default option is disable,

Because the CPU calculates the compilation warning, it takes time

– wno-complex-condition, by default, warning no is not required for this compilation group complex-condition

Enable compilation warnings for if checks using the option -wcomplex-condition

(This step, new debug, hereafter referred to as job_debug1)

/yourPath/llvm-project/build/bin/clang -Wcomplex-condition -c /xxx/test.cpp

3. Clang development, stage 2, warning processing of complex if statements

3.1. Calculate the complexity of the if nested statement

Back to our DSA

3.1.1 Perceptual knowledge: AST expansion of simple code statements
  • Function declaration f1, corresponding declaration expression, declaration reference expresssion

  • For the function F1, an implicit cast expression is made

I’ve got a pointer to f1

  • Get the result of the function pointer call, f1(), call expression

  • And then all sorts of simple operations

3.1.2 Calculate the complexity of if nested statements

The concern is binary operation nodes

If you hit level 3, you’re going to get an error

3.1.3 Modify the code
  • Compute the hierarchy and traverse the tree depth first
void DiagnoseIf(const Expr * IfRoot, Sema &S, const Expr * CurrentExpr, Int CurrentNestingLevel){int CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); If (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ Whether && and | | the 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{// Pair tree, depth-first traversal, with a simple recursive DiagnoseIf(IfRoot, S, BinaryOp->getLHS(), CurrentNestingLevel + 1); DiagnoseIf(IfRoot, S, BinaryOp->getRHS(), CurrentNestingLevel + 1); }}}}Copy the code
  • Call part
DiagnoseIf(Cond.Condition.get(), *this, Cond.Condition.get(), 0);
Copy the code
3.1.5 validation under

Compile job_O and debug job_debug1

  • The new case
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 }Copy the code
  • The results of

Examples 0 to 4 are normal

Example 5, warning is repeated,

Repeat warning, noisy

Simple algorithm error

So let me draw the corresponding tree

➜ build git:(main) Qualify /yourPath/llvm-project/build/bin/clang - wcomplex-condition -c/XXX /test.cpp/XXX /test.cpp:18:37: Warning: If statement, too complex, fix it... [-Wcomplex-condition] if (((a > 0 || b > 0) && c > 0) || d > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ / XXX /test. CPP :20:48: Warning: If statement [-Wcomplex-condition] if ((((a > 0 || b > 0) && c > 0) || d > 0) && e > 0){ } ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ ~ ~ ~ ~ ~ ~ ~ / XXX/test. The CPP: but: warning: if statements, too complex, under... [-Wcomplex-condition] if (((a > 0 || b > 0) && (c > 0 || d > 0)) || e > 0){ } ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ ~ ~ ~ ~ ~ ~ ~ / XXX/test. The CPP: but: warning: if statements, too complex, under... [-Wcomplex-condition] if (((a > 0 || b > 0) && (c > 0 || d > 0)) || e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ 4 warnings generated.Copy the code

3.2, fix bug 1, warning repeated

Cause, as shown in figure

Solve, encounter an error, end

Code changes

bool DiagnoseIf(const Expr * IfRoot, Sema &S, const Expr * CurrentExpr, Int CurrentNestingLevel){int CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); If (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ Whether && and | | the 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 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; }Copy the code
Change, see the effect

The process goes through,

Go through the two jobs once

Normal (same use case)

➜ build git:(main) Qualify /yourPath/llvm-project/build/bin/ dang-wcomplex-condition -c/XXX /test/XXX /test:18:37: Warning: If statement, too complex, fix it... [-Wcomplex-condition] if (((a > 0 || b > 0) && c > 0) || d > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ / XXX /test. CPP :20:48: Warning: If statement [-Wcomplex-condition] if ((((a > 0 || b > 0) && c > 0) || d > 0) && e > 0){ } ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ ~ ~ ~ ~ ~ ~ ~ / XXX/test. The CPP: but: warning: if statements, too complex, under... [-Wcomplex-condition] if (((a > 0 || b > 0) && (c > 0 || d > 0)) || e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ 3 warnings generated.Copy the code

4. Clang development, stage 3, continuous debug, new situation encountered

4.1. Consider how the AST is constructed

4.1.1 New use cases

Simple repetition, it seems, without nesting

if (a > 0 || b > 0 || c > 0 || d > 0 || e > 0){ }
Copy the code
4.1.2 Run (same process as above)
/ XXX /test. CPP :24:42: Warning: If statement [-Wcomplex-condition] if (a > 0 || b > 0 || c > 0 || d > 0 || e > 0){ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~Copy the code
4.1.3 Analyzing problems

C++ allows continuous judgments to be written without parentheses

In fact, this use case has an equivalent for semantic analysis

Thus the BINARY operator judgment of the AST is nested with three layers

You need to avoid that

4.1.4 to solve

Helper method

/ / / / nothing agreement, 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; }Copy the code

Modify method,

Add judgment if the node points of the parent binary operator and the node points of the child binary operator are equal

This layer counts, ignored

bool DiagnoseIf(const Expr * IfRoot, Sema &S, const Expr * CurrentExpr, Int CurrentNestingLevel){int CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); If (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ Whether && and | | the if (BinaryOp - > getOpcode () = = BO_LAnd | | BinaryOp - > getOpcode () = = BO_LOr) {/ / the tree depth-first traversal, There is a simple recursion int val = valOfExpresion(CurrentExpr); int valLhs = valOfExpresion(BinaryOp->getLHS()); Int levelLhs = 1; int levelLhs = 1; if (val == valLhs){ levelLhs = 0; } int valRhs = valOfExpresion(BinaryOp->getRHS()); int levelRhs = 1; if (val == valRhs){ levelRhs = 0; } 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; }Copy the code
4.1.5 Incorrect Demonstration

Prepend return

So nested 2 layers, this side of the error

Judgment for | |, &&, decided to return to again,

This error is reported only when this layer is considered, + 1 layer, 3 layer nesting

The value of levelLhs, which considers whether access to this layer should be included in the calculation or ignored

The value of levelRhs, as well

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; CurrentExpr = CurrentExpr->IgnoreParenImpCasts(); If (const auto * BinaryOp = dyn_cast<BinaryOperator>(CurrentExpr)){ Whether && and | | the if (BinaryOp - > getOpcode () = = BO_LAnd | | BinaryOp - > getOpcode () = = BO_LOr) {/ / the tree depth-first traversal, There is a simple recursion int val = valOfExpresion(CurrentExpr); int valLhs = valOfExpresion(BinaryOp->getLHS()); Int levelLhs = 1; 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; }Copy the code

False demonstration effect

/ XXX /test. CPP :16:26: Warning: If statement [-Wcomplex-condition] if ((a > 0 || b > 0) && c > 0){ } ~~~~~~~~~~~~~~~~~^~~~~~~~Copy the code

4.2. Other circumstances

4.2.1 macro expansion exists in the if statement

The unfolding of macros is done during preprocessing,

When we deal with AST, it is in the semantic analysis phase that we need to avoid

Solution, slightly

4.2.2, C++ template function, repeated error

Solution, slightly

5. Add clang to Xcode

It’s easy to do

  • Customize two user Settings

CC and CXX

  • Fill in the content

The path to CC is

/yourPath/llvm-projectX/build/bin/clang

The path to CXX is

/yourPath/llvm-projectX/build/bin/clang++

  • Sets the compile options for the file

To be warned

-Wcomplex-condition

github repo