Skip to content
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

Implement generics package #867

Merged
merged 15 commits into from
Mar 26, 2025
Merged
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
Expand Up @@ -13,7 +13,7 @@

import cpp
import codingstandards.c.cert
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers
import semmle.code.cpp.dataflow.TaintTracking
import ScaledIntegerPointerArithmeticFlow::PathGraph

Expand Down
8 changes: 3 additions & 5 deletions c/cert/src/rules/DCL40-C/IncompatibleFunctionDeclarations.ql
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import cpp
import codingstandards.c.cert
import codingstandards.cpp.Compatible
import codingstandards.cpp.types.Compatible
import ExternalIdentifiers

from ExternalIdentifiers d, FunctionDeclarationEntry f1, FunctionDeclarationEntry f2
Expand All @@ -29,12 +29,10 @@ where
f1.getName() = f2.getName() and
(
//return type check
not typesCompatible(f1.getType(), f2.getType())
not FunctionDeclarationTypeEquivalence<TypesCompatibleConfig>::equalReturnTypes(f1, f2)
or
//parameter type check
parameterTypesIncompatible(f1, f2)
or
not f1.getNumberOfParameters() = f2.getNumberOfParameters()
not FunctionDeclarationTypeEquivalence<TypesCompatibleConfig>::equalParameterTypes(f1, f2)
) and
// Apply ordering on start line, trying to avoid the optimiser applying this join too early
// in the pipeline
Expand Down
150 changes: 150 additions & 0 deletions c/common/src/codingstandards/c/Generic.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import cpp
import codingstandards.cpp.Macro
import codingstandards.cpp.MatchingParenthesis

string genericRegexp() { result = "\\b_Generic\\s*\\(\\s*(.+),.*" }

bindingset[input]
string deparenthesize(string input) {
input = "(" + result + ")" and
result = input.substring(1, input.length() - 1)
}

class GenericMacro extends Macro {
string ctrlExpr;

GenericMacro() { ctrlExpr = getBody().regexpCapture(genericRegexp(), 1).trim() }

string getAParameter() { result = this.(FunctionLikeMacro).getAParameter() }

string getControllingExprString() {
if exists(string s | s = deparenthesize(ctrlExpr))
then result = deparenthesize(ctrlExpr).trim()
else result = ctrlExpr
}

/**
* Whether the controlling expression of the `_Generic` expr in this macro's controlling
* expression refers to one of this macro's parameters.
*/
predicate hasControllingExprFromMacroParameter() {
getControllingExprString().matches(getAParameter())
}
}

class GenericMacroString extends string {
GenericMacroString() { this = any(Macro m).getBody() and this.matches("%_Generic%") }
}

import MatchingParenthesis<GenericMacroString>

class ParsedGenericMacro extends Macro {
ParsedRoot macroBody;
Parsed genericBody;
string beforeGenericBody;
string afterGenericBody;

ParsedGenericMacro() {
macroBody.getInputString() = this.getBody() and
exists(ParsedText genericText |
genericText.getText().matches("%_Generic%") and
genericBody = genericText.getParent().getChild(genericText.getChildIdx() + 1) and
genericBody.getRoot() = macroBody
) and
beforeGenericBody =
textFrom(macroBody.getStartToken(), genericBody.getStartToken().getPrevious()) and
(
if exists(genericBody.getEndToken().getNext())
then afterGenericBody = textFrom(genericBody.getEndToken().getNext(), macroBody.getEndToken())
else afterGenericBody = ""
)
}

string getAParameter() { result = this.(FunctionLikeMacro).getAParameter() }

int getAParsedGenericCommaSeparatorOffset() {
exists(ParsedText text |
text.getParent() = genericBody and
result = text.getStartToken().getStartPos() + text.getText().indexOf(",")
)
}

int getAParsedGenericColonSeparatorOffset() {
exists(ParsedText text |
text.getParent() = genericBody and
result = text.getStartToken().getStartPos() + text.getText().indexOf(":")
)
}

int getParsedGenericCommaSeparatorOffset(int i) {
result = rank[i](int index | index = getAParsedGenericCommaSeparatorOffset())
}

bindingset[start, end]
int getParsedGenericColon(int start, int end) {
result =
min(int offset |
offset = getAParsedGenericColonSeparatorOffset() and
offset >= start and
offset <= end
)
}

predicate hasParsedFullSelectionRange(int idx, int start, int end) {
idx = 1 and
start = genericBody.getStartToken().getEndPos() and
end = getParsedGenericCommaSeparatorOffset(idx)
or
not exists(getParsedGenericCommaSeparatorOffset(idx)) and
start = getParsedGenericCommaSeparatorOffset(idx - 1) and
end = genericBody.getEndToken().getStartPos()
or
start = getParsedGenericCommaSeparatorOffset(idx - 1) and
end = getParsedGenericCommaSeparatorOffset(idx)
}

string getSelectionString(int idx) {
exists(int start, int rawStart, int end |
hasParsedFullSelectionRange(idx, rawStart, end) and
(
if exists(getParsedGenericColon(rawStart, end))
then start = getParsedGenericColon(rawStart, end)
else start = rawStart
) and
result = genericBody.getInputString().substring(start, end)
)
}

string getControllingExprString() { result = getSelectionString(1).trim() }

bindingset[str, word]
private int countWordInString(string word, string str) {
result =
max(int occurrence |
exists(str.regexpFind("\\b" + word + "\\b", occurrence, _)) or occurrence = -1
|
occurrence + 1
)
}

int expansionsOutsideExpr(string parameter) {
parameter = getAParameter() and
result =
countWordInString(parameter, beforeGenericBody) +
countWordInString(parameter, afterGenericBody)
}

int expansionsInsideSelection(string parameter, int idx) {
parameter = getAParameter() and
result = countWordInString(parameter, getSelectionString(idx))
}

int expansionsInsideControllingExpr(string parameter) {
result = expansionsInsideSelection(parameter, 1)
}

int expansionsInsideAssociation(string parameter, int idx) {
not idx = 0 and
result = expansionsInsideSelection(parameter, idx + 1)
}
}
2 changes: 1 addition & 1 deletion c/common/src/codingstandards/c/OutOfBounds.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import cpp
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers
import codingstandards.c.Variable
import codingstandards.cpp.Allocations
import codingstandards.cpp.Overflow
Expand Down
2 changes: 1 addition & 1 deletion c/common/src/codingstandards/c/UndefinedBehavior.qll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import cpp
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers
import codingstandards.cpp.UndefinedBehavior

/**
Expand Down
18 changes: 9 additions & 9 deletions c/misra/src/codingstandards/c/misra/EssentialTypes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,14 @@ EssentialTypeCategory getEssentialTypeCategory(Type type) {
*/
pragma[nomagic]
Type getEssentialType(Expr e) {
if e.hasExplicitConversion()
then
if e.getConversion() instanceof ParenthesisExpr
then
if e.getConversion().(ParenthesisExpr).hasExplicitConversion()
then result = e.getConversion().(ParenthesisExpr).getConversion().getType()
else result = e.getConversion().(ParenthesisExpr).getExpr().(EssentialExpr).getEssentialType()
else result = e.getConversion().getType()
if e.hasConversion()
then result = getEssentialTypeOfConversion(e.getFullyConverted())
else result = e.(EssentialExpr).getEssentialType()
}

Type getEssentialTypeOfConversion(Expr e) {
if e.(Conversion).isImplicit() or e instanceof ParenthesisExpr or e instanceof C11GenericExpr
then result = getEssentialTypeOfConversion(e.(Conversion).getExpr())
else result = e.(EssentialExpr).getEssentialType()
}

Expand Down Expand Up @@ -455,7 +455,7 @@ class EssentialLiteral extends EssentialExpr, Literal {
if underlyingStandardType.(IntType).isSigned()
then result = stlr(this)
else result = utlr(this)
else result = underlyingStandardType
else result = getStandardType()
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers

from CStyleCast cast, Type type, Type newType
where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers
import codingstandards.cpp.Type

from Cast cast, Type type, Type newType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers

from CStyleCast cast, Type baseTypeFrom, Type baseTypeTo
where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import cpp
import codingstandards.c.misra
import codingstandards.cpp.Macro
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers

MacroInvocation getAMacroInvocation(CStyleCast cast) { result.getAnExpandedElement() = cast }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers

from Cast cast, VoidPointerType type, PointerToObjectType newType
where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers

from CStyleCast cast, Type typeFrom, Type typeTo
where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers

class MisraNonIntegerArithmeticType extends Type {
MisraNonIntegerArithmeticType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers
import codingstandards.cpp.Type

from Zero zero, Expr e, string type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.VariablyModifiedTypes
import codingstandards.cpp.types.VariablyModifiedTypes

from VmtDeclarationEntry v, string declstr, string adjuststr, string relationstr
where
Expand Down
2 changes: 1 addition & 1 deletion c/misra/src/rules/RULE-2-4/UnusedTagDeclaration.ql
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.TypeUses
import codingstandards.cpp.types.Uses

from UserType s
where
Expand Down
2 changes: 1 addition & 1 deletion c/misra/src/rules/RULE-2-8/UnusedObjectDefinition.ql
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ from ReportDeadObject report
where
not isExcluded(report.getPrimaryElement(), DeadCode2Package::unusedObjectDefinitionQuery()) and
not report.hasAttrUnused()
select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocation(),
select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(),
report.getOptionalPlaceholderMessage()
2 changes: 1 addition & 1 deletion c/misra/src/rules/RULE-2-8/UnusedObjectDefinitionStrict.ql
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ from ReportDeadObject report
where
not isExcluded(report.getPrimaryElement(), DeadCode2Package::unusedObjectDefinitionStrictQuery()) and
report.hasAttrUnused()
select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocation(),
select report.getPrimaryElement(), report.getMessage(), report.getOptionalPlaceholderLocatable(),
report.getOptionalPlaceholderMessage()
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import cpp
import codingstandards.c.misra
import codingstandards.cpp.Pointers
import codingstandards.cpp.types.Pointers

class MemCmpMoveCpy extends Function {
// Couldn't extend BuiltInFunction because it misses `memcmp`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @id c/misra/generic-selection-doesnt-depend-on-macro-argument
* @name RULE-23-1: A generic selection should depend on the type of a macro argument
* @description A generic selection should depend on the type of a macro argument.
* @kind problem
* @precision high
* @problem.severity warning
* @tags external/misra/id/rule-23-1
* correctness
* maintainability
* external/misra/c/2012/amendment3
* external/misra/obligation/advisory
*/

import cpp
import codingstandards.c.misra
import codingstandards.c.Generic

from ParsedGenericMacro macro, string ctrlExpr
where
not isExcluded(macro, GenericsPackage::genericSelectionDoesntDependOnMacroArgumentQuery()) and
ctrlExpr = macro.getControllingExprString() and
// No parameter exists that is expanded in the controlling expression one or more times
not exists(string parameter | macro.expansionsInsideControllingExpr(parameter) > 0)
select macro,
"Generic macro " + macro.getName() + " doesn't refer to a macro parameter in controlling expr '" +
ctrlExpr + "'."
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @id c/misra/generic-selection-not-expanded-from-a-macro
* @name RULE-23-1: A generic selection should only be expanded from a macro
* @description A generic selection should only be expanded from a macro.
* @kind problem
* @precision very-high
* @problem.severity warning
* @tags external/misra/id/rule-23-1
* maintainability
* external/misra/c/2012/amendment3
* external/misra/obligation/advisory
*/

import cpp
import codingstandards.c.misra

from C11GenericExpr generic, Expr ctrlExpr
where
not isExcluded(generic, GenericsPackage::genericSelectionNotExpandedFromAMacroQuery()) and
ctrlExpr = generic.getControllingExpr() and
not exists(MacroInvocation mi | mi.getAGeneratedElement() = generic.getExpr())
select generic, "$@ in generic expression does not expand a macro parameter.", ctrlExpr,
"Controlling expression"
Loading
Loading