Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- All rules using `Linkage.qll`:
- `extern const` global variables are now properly analyzed as having external linkage, rather than internal linkage.
- Linkage analysis has been fixed to properly handle nested classes, including anonymous and typedefs of anonymous classes.
- Linkage for names within classes with internal linkage is now properly inherited as internal, rather than external.
- `M0-1-3`, `RULE-2-8` - `UnusedLocalVariable.ql`, `UnusedMemberVariable.ql`, `UnusedGlobalOrNamespaceVariable.ql`, `UnusedObjectDefinition.ql`, `UnusedObjectDefinitionStrict.ql`:
- The organization of unused variable analysis has been reorganized to be usable in MISRA C++ rule 0.2.1, with no expected noticeable change in results.
- New filtering passes begin by filtering out variables that have an existing access (`.getAnAccess()`), instead of performing such a filter step last, with a measured small reduction in overall tuple counts and improved overall query performance.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.deadcode.UnusedVariables

from PotentiallyUnusedGlobalOrNamespaceVariable v
where
not isExcluded(v, DeadCodePackage::unusedGlobalOrNamespaceVariableQuery()) and
// No variable access
not exists(v.getAnAccess()) and
// Exclude members whose value is compile time and is potentially used to inintialize a template
not maybeACompileTimeTemplateArgument(v)
from ThirdPassUnused::UnusedGlobalOrNamespaceVariable v
where not isExcluded(v, DeadCodePackage::unusedGlobalOrNamespaceVariableQuery())
select v, "Variable '" + v.getQualifiedName() + "' is unused."
60 changes: 6 additions & 54 deletions cpp/autosar/src/rules/M0-1-3/UnusedLocalVariable.ql
Original file line number Diff line number Diff line change
Expand Up @@ -18,58 +18,10 @@ import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.deadcode.UnusedVariables

// Collect constant values that we should use to exclude otherwise unused constexpr variables.
//
// For constexpr variables used as template arguments or in static_asserts, we don't see accesses
// (just the appropriate literals). We therefore take a conservative approach and do not report
// constexpr variables whose values are used in such contexts.
//
// For performance reasons, these special values should be collected in a single pass.
predicate excludedConstantValue(string value) {
value = any(ClassTemplateInstantiation cti).getTemplateArgument(_).(Expr).getValue()
or
value = any(StaticAssert sa).getCondition().getAChild*().getValue()
}

/**
* Defines the local variables that should be excluded from the unused variable analysis based
* on their constant value.
*
* See `excludedConstantValue` for more details.
*/
predicate excludeVariableByValue(Variable variable) {
variable.isConstexpr() and
excludedConstantValue(getConstExprValue(variable))
}

// TODO: This predicate may be possible to merge with M0-1-4's getUseCount(). These two rules
// diverged to handle `excludeVariableByValue`, but may be possible to merge.
int getUseCountConservatively(Variable v) {
result =
count(VariableAccess access | access = v.getAnAccess()) +
count(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) +
// In case an array type uses a constant in the same scope as the constexpr variable,
// consider it as used.
countUsesInLocalArraySize(v)
}

predicate isConservativelyUnused(Variable v) {
getUseCountConservatively(v) = 0 and
not excludeVariableByValue(v)
}

from PotentiallyUnusedLocalVariable v
where
not isExcluded(v, DeadCodePackage::unusedLocalVariableQuery()) and
// Local variable is never accessed
not exists(v.getAnAccess()) and
// Sometimes multiple objects representing the same entities are created in
// the AST. Check if those are not accessed as well. Refer issue #658
not exists(LocalScopeVariable another |
another.getDefinitionLocation() = v.getDefinitionLocation() and
another.hasName(v.getName()) and
exists(another.getAnAccess()) and
another != v
) and
isConservativelyUnused(v)
// 10.1, 41.0
// 10.9, 45.9
// 11.0, 52.3
// 13.1, 59.3
from ThirdPassUnused::UnusedLocalVariable v
where not isExcluded(v, DeadCodePackage::unusedLocalVariableQuery())
select v, "Local variable '" + v.getName() + "' in '" + v.getFunction().getName() + "' is not used."
11 changes: 2 additions & 9 deletions cpp/autosar/src/rules/M0-1-3/UnusedMemberVariable.ql
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@ import codingstandards.cpp.autosar
import codingstandards.cpp.FunctionEquivalence
import codingstandards.cpp.deadcode.UnusedVariables

from PotentiallyUnusedMemberVariable v
where
not isExcluded(v, DeadCodePackage::unusedMemberVariableQuery()) and
// No variable access
not exists(v.getAnAccess()) and
// No explicit initialization in a constructor
not exists(UserProvidedConstructorFieldInit cfi | cfi.getTarget() = v) and
// Exclude members whose value is compile time and is potentially used to inintialize a template
not maybeACompileTimeTemplateArgument(v)
from ThirdPassUnused::UnusedMemberVariable v
where not isExcluded(v, DeadCodePackage::unusedMemberVariableQuery())
select v, "Member variable '" + v.getName() + "' is unused."
206 changes: 90 additions & 116 deletions cpp/common/src/codingstandards/cpp/Linkage.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,35 @@
import cpp
import codingstandards.cpp.Scope

/**
* [basic]/6 uses a specific definition of "variable" that excludes non-static data members.
*/
private predicate isSpecificationVariable(Variable v) {
v.(MemberVariable).isStatic()
or
not v instanceof MemberVariable
}

/** Holds if `elem` has internal linkage. */
predicate hasInternalLinkage(Element elem) {
// An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage
directlyOrIndirectlyUnnnamedNamespace(elem)
or
exists(Declaration decl | decl = elem |
// A name having namespace scope has internal linkage if it is the name of
hasNamespaceScope(decl) and
(
// a variable, function or function template
(
decl instanceof Variable
isSpecificationVariable(decl)
or
decl instanceof Function // TemplateFunction is a subclass of Function so this captures both.
) and
// that is explicitly declared static; or,
decl.isStatic()
or
// a non-volatile variable
decl instanceof Variable and
isSpecificationVariable(decl) and
not decl.(Variable).isVolatile() and
// that is explicitly declared const or constexpr and
(decl.(Variable).isConst() or decl.(Variable).isConstexpr()) and
Expand All @@ -32,131 +44,93 @@ predicate hasInternalLinkage(Element elem) {
exists(Union u | hasNamespaceScope(u) and u.isAnonymous() |
decl = u.getACanonicalMemberVariable()
)
or
// A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
hasInternalLinkage(decl.getNamespace()) and
(
// a variable;
decl instanceof Variable
or
// a function
decl instanceof Function
or
// a named class, or an unnamed class defined in a typedef declartion in which the class has the typedef name for linkage purposes
exists(Class klass | decl = klass |
not klass.isAnonymous()
or
klass.isAnonymous() and exists(TypedefType typedef | typedef.getADeclaration() = klass)
)
or
// a named enumeration, or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes
exists(Enum enum | enum = decl |
not enum.isAnonymous()
or
enum.isAnonymous() and exists(TypedefType typedef | typedef.getADeclaration() = enum)
)
or
// an enumerator beloning to an enumeration with linkage
exists(Enum enum | enum.getADeclaration() = decl | hasInternalLinkage(enum))
or
// a template
decl instanceof TemplateClass
or
decl instanceof TemplateFunction
)
)
or
decl instanceof GlobalVariable and
(
decl.(GlobalVariable).isStatic() or
decl.(GlobalVariable).isConst()
) and
not decl.(GlobalVariable).hasSpecifier("external")
)
or
// An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage
exists(Namespace ns | ns = elem |
ns.isAnonymous()
directlyOrIndirectlyUnnnamedNamespace(decl.getNamespace()) and
inheritsLinkageOfNamespace(decl.getNamespace(), decl)
or
not ns.isAnonymous() and
exists(Namespace parent | parent.isAnonymous() and parent.getAChildNamespace+() = ns)
exists(Class klass |
hasInternalLinkage(klass) and
inheritsLinkageOfClass(klass, decl)
)
)
or
elem instanceof TopLevelFunction and
elem.(Function).isStatic()
}

/** Holds if `elem` has external linkage. */
predicate hasExternalLinkage(Element elem) {
elem instanceof Namespace and
not directlyOrIndirectlyUnnnamedNamespace(elem)
or
not hasInternalLinkage(elem) and
(
exists(Declaration decl | decl = elem |
hasNamespaceScope(decl) and
// A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
not hasInternalLinkage(decl.getNamespace()) and
(
// a variable;
decl instanceof Variable
or
// a function
decl instanceof Function
or
// a named class, or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes
exists(Class klass | decl = klass |
not klass.isAnonymous()
or
klass.isAnonymous() and exists(TypedefType typedef | typedef.getADeclaration() = klass)
)
or
// a named enumeration, or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes
exists(Enum enum | enum = decl |
not enum.isAnonymous()
or
enum.isAnonymous() and exists(TypedefType typedef | typedef.getADeclaration() = enum)
)
or
// an enumerator beloning to an enumeration with linkage
exists(Enum enum | enum.getADeclaration() = decl | hasInternalLinkage(enum))
or
// a template
decl instanceof TemplateClass
or
decl instanceof TemplateFunction
)
or
// In addition,
hasClassScope(decl) and
(
// a member function,
decl instanceof MemberFunction
or
// static data member
decl instanceof MemberVariable and decl.(MemberVariable).isStatic()
or
// a named class, or an unnamed class defined in a typedef declartion in which the class has the typedef name for linkage purposes
exists(Class klass | decl = klass |
not klass.isAnonymous()
or
klass.isAnonymous() and exists(TypedefType typedef | typedef.getADeclaration() = klass)
)
or
// a named enumeration, or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes
exists(Enum enum | enum = decl |
not enum.isAnonymous()
or
enum.isAnonymous() and exists(TypedefType typedef | typedef.getADeclaration() = enum)
)
) and
// has external linkage if the name of the class has external linkage
hasExternalLinkage(decl.getDeclaringType())
or
decl instanceof GlobalVariable and
not decl.(GlobalVariable).isStatic() and
not decl.(GlobalVariable).isConst()
exists(Declaration decl | decl = elem |
// A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
not directlyOrIndirectlyUnnnamedNamespace(decl.getNamespace()) and
inheritsLinkageOfNamespace(decl.getNamespace(), decl)
or
exists(Class klass |
hasExternalLinkage(klass) and
inheritsLinkageOfClass(klass, decl)
)
)
}

private predicate directlyOrIndirectlyUnnnamedNamespace(Namespace ns) {
exists(Namespace anonymous |
anonymous.isAnonymous() and
ns = anonymous.getAChildNamespace*()
)
}

private predicate hasLinkageOfTypedef(TypedefType typedef, Element decl) {
// an unnamed class defined in a typedef declartion in which the class has the typedef name for linkage purposes
decl.(Class).isAnonymous() and typedef.getADeclaration() = decl
or
// an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes
decl.(Enum).isAnonymous() and typedef.getADeclaration() = decl
}

private predicate inheritsLinkageOfNamespace(Namespace ns, Declaration decl) {
hasNamespaceScope(decl) and
ns = decl.getNamespace() and
(
// A name having namespace scope that has not been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
// a variable;
isSpecificationVariable(decl)
or
// a function
decl instanceof Function
or
decl instanceof Class and not decl.(Class).isAnonymous() // a named class
or
decl instanceof Enum and not decl.(Enum).isAnonymous() // a named enumeration
or
// a template
decl instanceof TemplateClass
or
decl instanceof TemplateFunction
or
decl instanceof TemplateVariable
)
or
hasNamespaceScope(decl) and
exists(TypedefType typedef | hasLinkageOfTypedef(typedef, decl) and ns = typedef.getNamespace())
}

private predicate inheritsLinkageOfClass(Class klass, Element decl) {
hasClassScope(decl) and
(
// a member function,
decl.(MemberFunction).getDeclaringType() = klass
or
// static data member
decl.(MemberVariable).isStatic() and decl.(MemberVariable).getDeclaringType() = klass
or
elem instanceof Namespace
decl.(Class).getDeclaringType() = klass and not decl.(Class).isAnonymous()
or
elem instanceof TopLevelFunction
decl.(Enum).getDeclaringType() = klass and not decl.(Enum).isAnonymous()
or
exists(TypedefType typedef |
hasLinkageOfTypedef(typedef, decl) and klass = typedef.getDeclaringType()
)
)
}
9 changes: 3 additions & 6 deletions cpp/common/src/codingstandards/cpp/deadcode/UnusedObjects.qll
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,15 @@ import codingstandards.cpp.alertreporting.DeduplicateMacroResults
class UnusedObjectDefinition extends VariableDeclarationEntry {
UnusedObjectDefinition() {
(
getVariable() instanceof BasePotentiallyUnusedLocalVariable
getVariable() instanceof FirstPassUnused::UnusedLocalVariable
or
getVariable() instanceof BasePotentiallyUnusedGlobalOrNamespaceVariable
getVariable() instanceof FirstPassUnused::UnusedGlobalOrNamespaceVariable
) and
not exists(VariableAccess access | access.getTarget() = getVariable()) and
getVariable().getDefinition() = this
}

/* Dead objects with these attributes are reported in the "strict" queries. */
predicate hasAttrUnused() {
getVariable().getAnAttribute().hasName(["unused", "used", "maybe_unused", "cleanup"])
}
predicate hasAttrUnused() { hasAttrUnused(getVariable()) }
}

/* Configuration to use the `DedupMacroResults` module to reduce alert noise */
Expand Down
Loading
Loading