Another way to add compilation warnings: develop clang

Keywords: iOS

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

github repo

Posted by AsiaUnderworld on Wed, 01 Sep 2021 15:58:07 -0700