Skip to content

M0-1-3: Local variables that are used are reported as unused #658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rak3-sh opened this issue Aug 5, 2024 · 5 comments · Fixed by #660
Closed

M0-1-3: Local variables that are used are reported as unused #658

rak3-sh opened this issue Aug 5, 2024 · 5 comments · Fixed by #660
Assignees
Labels
Difficulty-Medium A false positive or false negative report which is expected to take 1-5 days effort to address false positive/false negative An issue related to observed false positives or false negatives. Impact-Medium user-report Issue reported by an end user of CodeQL Coding Standards

Comments

@rak3-sh
Copy link
Contributor

rak3-sh commented Aug 5, 2024

Affected rules

  • M0-1-3

Description

Local variables that are used are reported as unused. Please refer to the example for the sample scenarios.

Example

int foo()
{
  constexpr int arrayDim=10; // 'arrayDim' reported unused
  static int array[arrayDim] {}; // 'arrayDim' is used here
  return array[4];
}

template<typename T>
static T template_function()
{
  return T();
}

template<typename T, typename First, typename... Rest>           
static T template_function(const First& first, const Rest&... rest) // 'rest' reported unused
{
  return first + template_function<T>(rest...); // 'rest' is used here
}

static int templ_fnc2() {
  return template_function<int>(1, 2, 3, 4, 5);
}

int main()
{
  return 0;
}
@rak3-sh rak3-sh added the false positive/false negative An issue related to observed false positives or false negatives. label Aug 5, 2024
@rak3-sh
Copy link
Contributor Author

rak3-sh commented Aug 5, 2024

Root Cause

  1. The usage in array size is not considered in getUseCountConservatively function.
  2. For the 'rest' variable, there are two instances of Variable objects that get generated - one of type Parameter and the other of type LocalVariable. The name and location of the definition for both these objects are same (as checked by getName() and getDefinitionLocation() functions), but the getAnAccess API returns an access for Parameter object but not for the LocalVariable object.

Fix Strategy

(1) Consider the usage in array size in the getUseCountConservatively function like the following:

int getUseCountConservatively(Variable v) {
  result =
    count(VariableAccess access | access = v.getAnAccess()) 
      +
      ... <existing logic snipped>
      +
      // In case an array type uses a constant in the same scope as the constexpr variable,
      // consider it as used.
      count(ArrayType at, LocalVariable arrayVariable |
        arrayVariable.getType().resolveTypedefs() = at and
        v.(PotentiallyUnusedLocalVariable).getFunction() = arrayVariable.getFunction() and
        at.getArraySize().toString() = getConstExprValue(v)
      )
}

(2) Consider checking the usage of Parameter objects that have an access and logically same as the PotentiallyUnusedLocalVariable in UnusedVariable.qll like the following:

class PotentiallyUnusedLocalVariable extends LocalVariable {
  PotentiallyUnusedLocalVariable() {
    // Ignore variables declared in macro expansions
    not exists(DeclStmt ds | ds.getADeclaration() = this and ds.isInMacroExpansion()) 
    and
    ... <existing logic snipped>
    and
    not exists(Variable another |
      // Other variable has an access
      exists(another.getAnAccess()) and
      // Other variable has the same name
      another.hasName(this.getName()) and
      // Other variable is defined at the same location as this.
      another.getDefinitionLocation() = this.getDefinitionLocation()
    )
  }
}

@lcartey
Copy link
Collaborator

lcartey commented Aug 5, 2024

Thanks @rak3-sh!

I think 1. seems sensible to me.

For 2., I'd suggest applying some of the same restrictions as in 1. to ensure good performance:

  • Use LocalVariable instead of Variable as the type.
  • Restrict results to variables within the same function.

@rak3-sh
Copy link
Contributor Author

rak3-sh commented Aug 6, 2024

Thanks @lcartey! Following are my observations for (2).

  1. Using LocalVariable is not working because the other variant is of type Parameter which is at the same level in the type hierarchy as LocalVariable and hence the false positive on the variable rest in the example above is still reported. Instead, LocalScopeVariable seems to be a better option since both LocalVariable and Parameter are derived from LocalScopeVariable.

  2. When I use LocalScopeVariable and use the getFunction to match the functions, the false positive on rest in the example above is still reported. I am guessing it uses a different Function object and hence cannot be matched (?) because the getFunction().toString() results in the same function name but the equals operator returns false.

The modified fix that works for (2) looks like the following:

class PotentiallyUnusedLocalVariable extends LocalVariable {
  PotentiallyUnusedLocalVariable() {
    // Ignore variables declared in macro expansions
    not exists(DeclStmt ds | ds.getADeclaration() = this and ds.isInMacroExpansion()) and
    ... <existing logic snipped >
    exists(Function f | f = getFunction() |
    ... <existing logic snipped >
    )
    and
    not exists(LocalScopeVariable another |
      another.getDefinitionLocation() = this.getDefinitionLocation()
      and
      another.hasName(this.getName())
      and
      exists(another.getAnAccess())
    )
  }
}

@rak3-sh
Copy link
Contributor Author

rak3-sh commented Aug 6, 2024

Another observation is that if I modify getConstExprValue function to consider const variables as well, then the expected false positive in the query's test case will also be removed. I suppose its safe to do the following. Kindly let me know.

private string getConstExprValue(Variable v) {
  result = v.getInitializer().getExpr().getValue()
  and
  (
    // Check for const or constexpr. The test case has a const variable.
    v.isConst() or v.isConstexpr()
  )
}

rak3-sh added a commit to rak3-sh/codeql-coding-standards that referenced this issue Aug 6, 2024
@rak3-sh
Copy link
Contributor Author

rak3-sh commented Aug 6, 2024

Candidate fix is submitted at PR #660.

@lcartey lcartey added user-report Issue reported by an end user of CodeQL Coding Standards Difficulty-Medium A false positive or false negative report which is expected to take 1-5 days effort to address Impact-Medium labels Oct 18, 2024
@lcartey lcartey moved this from Reported to Done in Coding Standards Public Development Board Oct 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty-Medium A false positive or false negative report which is expected to take 1-5 days effort to address false positive/false negative An issue related to observed false positives or false negatives. Impact-Medium user-report Issue reported by an end user of CodeQL Coding Standards
Projects
2 participants