주요 콘텐츠

Detect Semantic Issues Using Polyspace Query Language Semantic Classes

Semantic issues arise when your code is syntactically correct but behaves in an unexpected manner. Examples of semantic issues include:

  • Discarding the returned value of a specific function

  • Unused variables

  • Multiple definitions of identical macros

  • Unexpected filenames or file extensions

Polyspace Query Language (PQL) has classes that model semantic objects in C/C++ code such as call sites, fields, variables, functions, macros, and namespaces. These classes have predicates that queries the code to find specific semantic objects. You can construct user-defined defects that look for semantic issues by using logical combination of these predicates. For a list of semantic classes, see Create Your Own Coding Rules and Coding Standard.

This example shows how to define a semantic issue and create a user-defined to detect the issue using PQL. The example defect detects whether variable names follow a specific naming convention.

Define Issue

Define the issue using plain language in as much details as possible. For this example,define the naming convention explicitly:

  • Variable names must start with a prefix.

  • The first letter of the prefix must indicate storage duration of the variable: g (global), s (static), or t (local nonstatic).

  • The next part of the prefix must indicate the data type of the variable: u (unsigned), s (signed), or st (structured).

  • Finally, if the variable is of an integer type, the prefix must contain number of bytes used for storage.

From this informal definition, you can derive what the defect needs to accomplish. At a high level, the defect needs to detect all variables in your code and extract their names. It also needs to identify the storage duration, data type, and size of the variables. Finally, it needs to calculate the expected prefix and compare it to the actual prefix of the variable name.

Define Defect Implementation Architecture

In this step, build on the issue definition to define the defect architecture. It is helpful to think of the defect as a filter that starts with all the semantic objects in your code. Each condition you add to the defect narrows the search until the desired objects are identified. Break the defect into smaller parts that performs intermediate steps. For example, this defect can contain these parts:

  1. Identify all variables in your code.

  2. Extract the names of variables as strings.

  3. Find the storage duration of a variable. Determine the first letter of the expected prefix from the storage duration.

  4. Find the data type of a variable. Determine the second part of the expected prefix from the data type.

  5. If the variable is of an integer type, find its size in bytes. Determine the number at the end of the expected prefix from the size.

  6. Combine the expected prefix into a string.

  7. Compare the actual prefix of the variable name with the calculates prefix.

  8. Report a defect on variables when the actual prefix does not match the expected prefix.

Outline Implementation of Components

Once you know the parts of the defects, review the predicates associated with the semantic PQL classes and identify how you can implement each part using their supported predicates. There can be multiple ways to implement a defect.

The implementation of a semantic defect uses these PQL classes:

Additionally, the helper class String provides predicates for string manipulation.

ComponentRelevant predicatesUser-defined predicate for the component

Identify all variables in your code and extract their names

  • Cpp.Variable.is

  • Cpp.Variable.name

This predicate queries variables, finds their names and returns the names as string objects.

predicate getVarName(Cpp.Variable.Variable variable, Lang.String &varname){
return variable.name(varname)
}

Find the storage duration of variables. Determine the first letter of the expected prefix using the storage duration.

  • Cpp.Variable.isGloballyVisible

  • Cpp.Variable.isFileStatic

  • Cpp.Variable.isFunctionStaticLocal

  • Cpp.Variable.isClassStatic

This predicate queries the variables and returns g if the variable is global, s if the function is static, and t if the variable is local nonstatic.

predicate getFirstLetter(Cpp.Variable.Variable variable, Lang.String &letter) {
	return
		(variable.isGloballyVisible() and Lang.createString( "g",&letter))
		or
		((variable.isFileStatic() or variable.isFunctionStaticLocal() or variable.isClassStatic()) and Lang.createString( "s", &letter)) 
		or
		(variable.isFunctionNonStaticLocal() and Lang.createString("t", &letter))
}

Find the data type of the variable. Determine the second part of the expected prefix using the data type.

  • Cpp.Type.isUnsigned

  • Cpp.Type.isSigned

  • Cpp.Type.isStruct

  • Cpp.Type.isClass

This predicate queries variables and checks their types. If the type is unsigned, the predicate returns u. If the type is class or struct, it returns st. If the type is signed it returns s.

predicate getSecondPart(Cpp.Variable.Variable variable, Lang.String &letter) {
	return
		variable.type(&type) and (
			(type.isUnsigned() and Lang.createString("u", &letter))
			or
			(type.isSigned() and Lang.createString("s", &letter))
			or
			((type.isStruct() or type.isClass()) and Lang.createString("st", &letter))
		)
}

For an integer variable, find the size in bytes. Determine the number at the end of the expected prefix using the size.

  • Cpp.Type.isIntegral

  • Cpp.Type.sizeInBytes

This predicates queries the type of a variable. If the variable is of an integral type, the predicate returns a string representing the size of the type in bytes.

predicate getThirdPart(Cpp.Variable.Variable variable, Lang.String &letter) {
	return
		variable.type(&type) and type.isIntegral()
		and type.sizeInBytes(&size) and Lang.toString(size, &letter)
}

Combine the expected prefix into a string. and check if the actual prefix of the variable name matches the prefix.

  • Lang.catString

  • Lang.startsWith

This predicate queries a variable and determines the expected prefix for the variable name. The predicate returns true if the actual variable name does not start with the calculated prefix:

predicate isNotCorrectPrefix(Cpp.Variable.Variable variable, Lang.String &prefix) {
	return
		variable.name(&varname)
		and getFirstLetter(variable, &F)
		and getSecondPart(variable, &S)
		and getThirdPart(variable, &T)
		and Lang.catString(F, S, &prefixA)
		and Lang.catString( prefixA, T, &prefix) 
		and not varname.startsWith(prefix)
}

Implement Defect

Implementing defects can be an iterative process. Execute the outlined predicates from the preceding step on sample C/C++ code iteratively until the defect behaves as expected. To facilitate this process, construct a small coding standard containing your queries. In a folder SemanticRule:

  1. Initialize a new user-defined coding standard by running this command:

    polyspace-query-language init

  2. Create a new folder Naming_Convention for the user-defined defect.

  3. Copy the predicates rule definition to individual pql files:

    •  Naming_Convention/getFirstLetter.pql

    •  Naming_Convention/getSecondPart.pql

    •  Naming_Convention/getThirdPart.pql

    •  Naming_Convention/isNotCorrectPrefix.pql

    •  Naming_Convention/test_defect.pql

    •  Naming_Convention/naming_convention.pql

  4. In the file main.pql, add this code to insert the rule testrule in a section:

    package main
    
    catalog TestStandard = {
    #[Description("Test Section")]
    	section TestSection = {
    		Naming_Convention.testrule
    	}
    }
    
    
    

  5. Add this code to a source file example.cpp. You can run and test your defect using this file:

     Example C++ code

    This code is annotated with expected rule violations.

  6. Build the defect in steps. As a sanity check, update the defect TestDefect in Naming_Convention/test_defect.pql so that it reports the name of all variables:

    package Naming_Convention
    defect TestDefect =
    when
      Cpp.Variable.is(&variable)
      and variable.name(&varname)
      raise "Variable detected \"{varname}\""
      on variable

    Package the standard into the file TestStandard.pschk and run a Polyspace® Bug Finder™ analysis using it:

    polyspace-query-language package
    polyspace-bug-finder -sources example.cpp -checkers-activation-file TestStandard.pschk 
    Open the results in Polyspace Platform user interface and verify that all variable names are correctly reported. Note that unused variables are ignored by Bug Finder.

  7. Update the defect in Naming_Convention/test_defect.pql to check if the predicate getFirstLetter behaves correctly.

    package Naming_Convention
    defect TestDefect =
    when
      Cpp.Variable.is(&variable) and getFirstLetter(variable, &letter)
      raise "First expected letter \"{letter}\""
      on variable
    Package the standard and run the Bug Finder analysis. Open the results in Polyspace Platform user interface and verify that the first letter of the variable names are correctly reported. No result is reported for the variable count. This is a class static variable. The predicate getFirstLetter does not account for this case. Add the predicate isClassStatic() to address this case. Update the predicate in Naming_Convention/getFirstLetter.pql to this:
    package Naming_Convention
    predicate getFirstLetter(Cpp.Variable.Variable variable, Lang.String &letter) {
    return
    	(variable.isGloballyVisible() and Lang.createString( "g",&letter))
    	or
    	((variable.isFileStatic() or variable.isFunctionStaticLocal() or variable.isClassStatic()) and Lang.createString( "s", &letter)) // Now detecting class static variables
    	or
    	(variable.isFunctionNonStaticLocal() and Lang.createString("t", &letter))
    }
    After packaging the coding standard and running the analysis, Bug Finder reports that the expected first letter of myclass::count is s, since this is a class static variable.

  8. Update the defect to check if the predicate getSecondPart behaves correctly. Update the defect TestDefet:

    package Naming_Convention
    defect TestDefect =
    when
      Cpp.Variable.is(&variable) and getSecondPart(variable, &letter)
      raise "Second expected part \"{letter}\""
      on variable
    Package the standard and run the Bug Finder analysis. In the Polyspace Platform user interface, verify that the reported second parts are correct.

  9. Update the defect to check if the predicate getThirdpart behaves correctly.

    package Naming_Convention
    defect TestDefect =
    when
      Cpp.Variable.is(&variable) and getThirdPart(variable, &letter)
      raise "Third expected part \"{letter}\""
      on variable
    Package the standard and run the Bug Finder analysis. In the Polyspace Platform user interface, verify that the reported third parts are correct.

  10. Complete the user-defined defect:

    package Naming_Convention
    defect TestDefect =
    when
      Cpp.Variable.is(&variable) and isNotCorrectPrefix(variable, &prefix)
      and variable.name(&name) and Lang.String.substr(name, 0, 3, &actual)
      raise "Expected prefix \"{prefix}\", actual prefix \"{actual}\""
      on variable
      }
    To contextualize the message, this defect extracts the actual prefix in variable names and reports it in the message.

    After packaging the coding standard and running a Bug Finder analysis, the variable names that are not compliant are reported as user-defined defects.

  11. Test the coding standard to verify the expected violations are present.

    polyspace-query-language test example.cpp
    The test passes with this output:
    number of actual defects:  6
    number of expected annotations:  13 (including 7 expected absence of defects).
    _______________________________________________
      Checking expected defects with actuals...
      -----------------------------------------
    
    
    _______________________________________________
      Looking for unexpected defects...
      ---------------------------------
    
    _______________________________________________
    
    Tests passed

See Also

Topics