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
Expand Up @@ -19,7 +19,7 @@
import cpp
import codingstandards.c.cert
import codingstandards.cpp.SideEffect
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.dataflow.new.TaintTracking
import semmle.code.cpp.valuenumbering.GlobalValueNumbering

/** Holds if the function's return value is derived from the `AliasParamter` p. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
| test.c:45:17:45:30 | ... + ... | Buffer may access up to offset 101*1 which is greater than the fixed size 100 of the $@. | test.c:45:17:45:22 | buffer | buffer |
| test.c:55:5:55:13 | ... - ... | Buffer access may be to a negative index in the buffer. | test.c:55:5:55:9 | ptr16 | buffer |
| test.c:57:5:57:14 | ... + ... | Buffer accesses offset 22 which is greater than the fixed size 20 of the $@. | test.c:57:5:57:9 | ptr16 | buffer |
| test.c:58:5:58:14 | ... - ... | Buffer access may be to a negative index in the buffer. | test.c:55:5:55:9 | ptr16 | buffer |
| test.c:58:5:58:14 | ... - ... | Buffer access may be to a negative index in the buffer. | test.c:56:5:56:9 | ptr16 | buffer |
| test.c:58:5:58:14 | ... - ... | Buffer access may be to a negative index in the buffer. | test.c:57:5:57:9 | ptr16 | buffer |
| test.c:58:5:58:14 | ... - ... | Buffer access may be to a negative index in the buffer. | test.c:58:5:58:9 | ptr16 | buffer |
| test.c:63:3:63:9 | access to array | Buffer access may be to a negative index in the buffer. | test.c:63:3:63:5 | arr | buffer |
| test.c:65:3:65:9 | access to array | Buffer accesses offset 44 which is greater than the fixed size 40 of the $@. | test.c:65:3:65:5 | arr | buffer |
| test.c:66:3:66:10 | access to array | Buffer access may be to a negative index in the buffer. | test.c:63:3:63:5 | arr | buffer |
| test.c:66:3:66:10 | access to array | Buffer access may be to a negative index in the buffer. | test.c:64:3:64:5 | arr | buffer |
| test.c:66:3:66:10 | access to array | Buffer access may be to a negative index in the buffer. | test.c:65:3:65:5 | arr | buffer |
| test.c:66:3:66:10 | access to array | Buffer access may be to a negative index in the buffer. | test.c:66:3:66:5 | arr | buffer |
Original file line number Diff line number Diff line change
@@ -1,25 +1 @@
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:28,31-39)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:28,59-67)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:31,33-41)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:31,57-65)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:35,33-41)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:35,59-67)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:44,5-13)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:44,25-33)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:44,53-61)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:47,31-39)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:47,57-65)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:56,31-39)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:56,55-63)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:63,31-39)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:63,57-65)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:75,31-39)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:75,55-63)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:28,5-18)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:31,7-20)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:35,7-20)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:47,5-18)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:56,5-18)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:63,5-18)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (DependenceOnOrderOfFunctionArgumentsForSideEffects.ql:75,5-18)
| test.c:20:3:20:4 | call to f1 | Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior. | test.c:20:6:20:7 | call to f2 | call to f2 | test.c:20:12:20:13 | call to f3 | call to f3 |
82 changes: 45 additions & 37 deletions c/common/src/codingstandards/c/OutOfBounds.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import codingstandards.cpp.Allocations
import codingstandards.cpp.Overflow
import codingstandards.cpp.PossiblyUnsafeStringOperation
import codingstandards.cpp.SimpleRangeAnalysisCustomizations
private import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.valuenumbering.GlobalValueNumbering

module OOB {
Expand Down Expand Up @@ -380,8 +379,13 @@ module OOB {
StrncatLibraryFunction() { this.getName() = getNameOrInternalName(["strncat", "wcsncat"]) }

override predicate getALengthParameterIndex(int i) {
// `strncat` and `wcsncat` exclude the size of a null terminator
i = 2
// The source need not contain a null terminator within the first `n` characters.
none()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be wrong. It's worth noting that StrncatLibraryFunction is the only class that overrides this predicate, and it's none() for all others, so it appears this was added for strncat specifically.

}

override predicate getANullTerminatedParameterIndex(int i) {
// The destination must be null-terminated.
i = 0
}
}

Expand Down Expand Up @@ -645,42 +649,46 @@ module OOB {
}

/**
* A class for reasoning about the offset of a variable from the original value flowing to it
* as a result of arithmetic or pointer arithmetic expressions.
* Gets the offset of `expr` from `underlyingBase` due to arithmetic or pointer arithmetic.
*
* `underlyingBase` may be the arithmetic operand's base expression or `expr` itself, allowing
* callers to use whichever dataflow node is available.
*/
bindingset[expr]
private int getArithmeticOffsetValue(Expr expr, Expr base) {
result = getMinStatedValue(expr.(PointerArithmeticExpr).getOperand()) and
base = expr.(PointerArithmeticExpr).getPointer()
or
// &(array[index]) expressions
result =
getMinStatedValue(expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getOperand()) and
base = expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getPointer()
or
result = getMinStatedValue(expr.(AddExpr).getRightOperand()) and
base = expr.(AddExpr).getLeftOperand()
or
result = -getMinStatedValue(expr.(SubExpr).getRightOperand()) and
base = expr.(SubExpr).getLeftOperand()
or
expr instanceof IncrementOperation and
result = 1 and
base = expr.(IncrementOperation).getOperand()
or
expr instanceof DecrementOperation and
result = -1 and
base = expr.(DecrementOperation).getOperand()
or
// fall-back if `expr` is not an arithmetic or pointer arithmetic expression
not expr instanceof PointerArithmeticExpr and
not expr.(AddressOfExpr).getOperand() instanceof PointerArithmeticExpr and
not expr instanceof AddExpr and
not expr instanceof SubExpr and
not expr instanceof IncrementOperation and
not expr instanceof DecrementOperation and
base = expr and
result = 0
private int getArithmeticOffsetValue(Expr expr, Expr underlyingBase) {
exists(Expr base | underlyingBase = [base, expr] |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love relying on this. It doesn't make sense to me why we would say that x + 1 is offset 1 from itself.

The missing node is from the definition of x to the usage of x in x + 1, is that right? We should be able to grab this node via something like node.asInstruction().(PointerAddInstruction).getAst() = ....

I think this is worth probing into more.

result = getMinStatedValue(expr.(PointerArithmeticExpr).getOperand()) and
base = expr.(PointerArithmeticExpr).getPointer()
or
// &(array[index]) expressions
result =
getMinStatedValue(expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getOperand()) and
base = expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getPointer()
or
result = getMinStatedValue(expr.(AddExpr).getRightOperand()) and
base = expr.(AddExpr).getLeftOperand()
or
result = -getMinStatedValue(expr.(SubExpr).getRightOperand()) and
base = expr.(SubExpr).getLeftOperand()
or
expr instanceof IncrementOperation and
result = 1 and
base = expr.(IncrementOperation).getOperand()
or
expr instanceof DecrementOperation and
result = -1 and
base = expr.(DecrementOperation).getOperand()
or
// fall-back if `expr` is not an arithmetic or pointer arithmetic expression
not expr instanceof PointerArithmeticExpr and
not expr.(AddressOfExpr).getOperand() instanceof PointerArithmeticExpr and
not expr instanceof AddExpr and
not expr instanceof SubExpr and
not expr instanceof IncrementOperation and
not expr instanceof DecrementOperation and
base = expr and
result = 0
)
}

private int constOrZero(Expr e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
problems
| test.c:11:7:11:12 | * ... | test.c:18:16:18:21 | call to getenv | test.c:11:7:11:12 | * ... | The object returned by the function getenv should not be modified. |
| test.c:11:8:11:12 | c_str | test.c:18:16:18:21 | call to getenv | test.c:11:7:11:12 | * ... | The object returned by the function getenv should not be modified. |
| test.c:67:5:67:9 | conv4 | test.c:64:11:64:20 | call to localeconv | test.c:67:5:67:9 | conv4 | The object returned by the function localeconv should not be modified. |
| test.c:76:5:76:8 | conv | test.c:72:25:72:34 | call to localeconv | test.c:76:5:76:8 | conv | The object returned by the function localeconv should not be modified. |
Expand Down
4 changes: 3 additions & 1 deletion c/misra/src/rules/RULE-14-3/ControllingExprInvariant.ql
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ where
conditionAlwaysFalse(expr) and
not (
getEssentialTypeCategory(getEssentialType(expr)) instanceof EssentiallyBooleanType and
expr.getValue() = "0"
expr.getValue() = "0" and
// Only apply to expressions that do not reference variables.
not exists(VariableAccess va | va = expr.getAChild*())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch, and great test case to go with this!

Note that this will cause false positives for while(sizeof(x) == ...), similarly for _Alignof(x). In general, I try to avoid getAChild*()/getParent*(), as they tend to be a bigger hammer than we often want, as these cases show.

c17 defines:

Constant expressions shall not contain assignment, increment, decrement, function-call, or comma
operators, except when they are contained within a subexpression that is not evaluated.

An integer constant expression(119) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts.
Cast operators in an integer constant expression shall only convert arithmetic types to integer types,
except as part of an operand to the sizeof or _Alignof operator.

It looks like we can basically add .isInUnevaluatedContext(). We could also check for function calls and maybe the comma operator. I think that would basically cover everything. At that point the only way to do things like increment and assignment would be really weird cases like *((int*)0x123) = 345 to get an lvalue.

)
or
conditionAlwaysTrue(expr) and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
| test.c:16:9:16:13 | ... > ... | Controlling expression in if statement has an invariant value. |
| test.c:20:20:20:24 | ... < ... | Controlling expression in loop statement has an invariant value. |
| test.c:27:10:27:14 | ... < ... | Controlling expression in loop statement has an invariant value. |
| test.c:37:3:37:6 | 1 | Controlling expression in conditional statement has an invariant value. |
| test.c:38:3:38:3 | 1 | Controlling expression in conditional statement has an invariant value. |
| test.c:45:10:45:26 | ... && ... | Controlling expression in loop statement has an invariant value. |
| test.c:35:12:35:12 | 0 | Controlling expression in loop statement has an invariant value. |
| test.c:39:3:39:6 | 1 | Controlling expression in conditional statement has an invariant value. |
| test.c:40:3:40:3 | 1 | Controlling expression in conditional statement has an invariant value. |
| test.c:47:10:47:26 | ... && ... | Controlling expression in loop statement has an invariant value. |
| test.c:49:10:49:21 | ... && ... | Controlling expression in loop statement has an invariant value. |
8 changes: 6 additions & 2 deletions c/misra/test/rules/RULE-14-3/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ void f1(int p1) {

void f2() {
while (20 > 10) { // NON_COMPLIANT
if (1 > 2) {
} // NON_COMPLIANT
if (1 > 2) { // NON_COMPLIANT
}
}

for (int i = 10; i < 5; i++) { // NON_COMPLIANT
Expand All @@ -31,6 +31,8 @@ void f3() {
void f4() {
do {
} while (0u == 1u); // COMPLIANT - by exception 2
do {
} while (0); // NON_COMPLIANT - a bare literal `0` is not essentially Boolean

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a test for do { } while(false);? Since false is usually #defined to 0.

}

void f5(bool b1) {
Expand All @@ -44,4 +46,6 @@ void f6(int p1) {
}
while (1 == 0 && p1 > 12) { // NON_COMPLIANT
}
while (0 && p1 > 12) { // NON_COMPLIANT

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, nice catch!

}
}
8 changes: 7 additions & 1 deletion c/misra/test/rules/RULE-21-18/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,10 @@ void test(void) {
strxfrm(buf + 1, buf2,
sizeof(buf) - 1); // NON_COMPLIANT - not null-terminated
}
}
}

void test_strncat_bounded_source(void) {
char destination[2] = {0};
char source[1] = {'x'};
strncat(destination, source, 1); // COMPLIANT
Comment thread
mbaluda marked this conversation as resolved.
}
9 changes: 9 additions & 0 deletions change_notes/2026-08-28-cpp-all-upgrade-result-changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
- `ENV30-C`, `RULE-21-19`, `RULE-25-5-2`: removed duplicate alerts for the same modification of a
pointer returned by an environment or locale function.
- `RULE-14-3`: loop controlling expressions with an invariant false value are now reported when
they use the integer literal `0`, including within compound expressions.
- `ARR30-C`: negative out-of-bounds accesses may now produce a result for each reaching buffer
expression.
- `ARR38-C`, `RULE-21-17`, `RULE-21-18`, `RULE-8-7-1`: corrected the modeling of `strncat` and
`wcsncat`. Their destination must be null-terminated, while their source does not need a null
terminator within the specified character limit.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.SmartPointers
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.dataflow.new.TaintTracking
import SingleObjectSmartPointerArrayConstructionFlow::PathGraph

class AutosarSmartPointerArraySpecialisation extends AutosarSmartPointer {
Expand All @@ -32,7 +32,7 @@ module SingleObjectSmartPointerArrayConstructionConfig implements DataFlow::Conf
mf.getDeclaringType() instanceof AutosarSmartPointerArraySpecialisation and
mf instanceof AutosarSmartPointerReleaseMemberFunction
|
fc.getParent()
fc
)
}

Expand All @@ -47,21 +47,14 @@ module SingleObjectSmartPointerArrayConstructionConfig implements DataFlow::Conf
)
}

predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node sink) {
exists(AutosarUniquePointer sp, FunctionCall fc |
fc = sp.getAReleaseCall() and
source.asExpr() = fc.getQualifier() and
sink.asExpr() = fc
)
}

predicate isBarrierIn(DataFlow::Node node) {
// Exclude flow into header files outside the source archive which are summarized by the
// additional taint steps above.
// Exclude flow through `release()` implementations summarized by its taint model.
exists(AutosarUniquePointer sp |
sp.getAReleaseCall().getTarget() = node.asExpr().(ThisExpr).getEnclosingFunction()
|
not exists(node.getLocation().getFile().getRelativePath())
sp.getAReleaseCall().getTarget() =
[
node.asExpr().(ThisExpr).getEnclosingFunction(),
node.asIndirectExpr().(ThisExpr).getEnclosingFunction()
]
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:26,67-75)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:27,22-30)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:39,20-28)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:50,34-42)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:50,57-65)
WARNING: module 'DataFlow' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:58,25-33)
WARNING: module 'TaintTracking' has been deprecated and may be removed in future (PointerToAnElementOfAnArrayPassedToASmartPointer.ql:70,3-16)
edges
| test.cpp:3:36:3:45 | new[] | test.cpp:19:27:19:44 | call to allocate_int_array | provenance | |
| test.cpp:3:36:3:45 | new[] | test.cpp:23:12:23:29 | call to allocate_int_array | provenance | |
| test.cpp:3:36:3:45 | new[] | test.cpp:27:20:27:37 | call to allocate_int_array | provenance | |
| test.cpp:11:29:11:41 | call to unique_ptr | test.cpp:12:27:12:28 | v2 | provenance | |
| test.cpp:12:27:12:28 | v2 | test.cpp:12:30:12:36 | call to release | provenance | |
| test.cpp:12:27:12:28 | v2 | test.cpp:12:30:12:36 | call to release | provenance | Config |
| test.cpp:3:6:3:23 | *allocate_int_array | test.cpp:19:27:19:44 | call to allocate_int_array | provenance | |
| test.cpp:3:6:3:23 | *allocate_int_array | test.cpp:23:12:23:29 | call to allocate_int_array | provenance | |
| test.cpp:3:6:3:23 | *allocate_int_array | test.cpp:27:20:27:37 | call to allocate_int_array | provenance | |
| test.cpp:3:36:3:45 | new[] | test.cpp:3:6:3:23 | *allocate_int_array | provenance | |
| test.cpp:3:36:3:45 | new[] | test.cpp:3:36:3:45 | new[] | provenance | |
| test.cpp:27:20:27:37 | call to allocate_int_array | test.cpp:27:20:27:37 | call to allocate_int_array | provenance | |
| test.cpp:27:20:27:37 | call to allocate_int_array | test.cpp:32:12:32:20 | int_array | provenance | |
nodes
| test.cpp:3:6:3:23 | *allocate_int_array | semmle.label | *allocate_int_array |
| test.cpp:3:36:3:45 | new[] | semmle.label | new[] |
| test.cpp:3:36:3:45 | new[] | semmle.label | new[] |
| test.cpp:11:29:11:41 | call to unique_ptr | semmle.label | call to unique_ptr |
| test.cpp:12:27:12:28 | v2 | semmle.label | v2 |
| test.cpp:12:30:12:36 | call to release | semmle.label | call to release |
| test.cpp:19:27:19:44 | call to allocate_int_array | semmle.label | call to allocate_int_array |
| test.cpp:23:12:23:29 | call to allocate_int_array | semmle.label | call to allocate_int_array |
| test.cpp:27:20:27:37 | call to allocate_int_array | semmle.label | call to allocate_int_array |
| test.cpp:27:20:27:37 | call to allocate_int_array | semmle.label | call to allocate_int_array |
| test.cpp:32:12:32:20 | int_array | semmle.label | int_array |
subpaths
#select
| test.cpp:12:30:12:36 | call to release | test.cpp:11:29:11:41 | call to unique_ptr | test.cpp:12:30:12:36 | call to release | A pointer to an element of an array of objects flows to a smart pointer of a single object type. |
| test.cpp:12:30:12:36 | call to release | test.cpp:12:30:12:36 | call to release | test.cpp:12:30:12:36 | call to release | A pointer to an element of an array of objects flows to a smart pointer of a single object type. |
| test.cpp:19:27:19:44 | call to allocate_int_array | test.cpp:3:36:3:45 | new[] | test.cpp:19:27:19:44 | call to allocate_int_array | A pointer to an element of an array of objects flows to a smart pointer of a single object type. |
| test.cpp:23:12:23:29 | call to allocate_int_array | test.cpp:3:36:3:45 | new[] | test.cpp:23:12:23:29 | call to allocate_int_array | A pointer to an element of an array of objects flows to a smart pointer of a single object type. |
| test.cpp:32:12:32:20 | int_array | test.cpp:3:36:3:45 | new[] | test.cpp:32:12:32:20 | int_array | A pointer to an element of an array of objects flows to a smart pointer of a single object type. |
Loading
Loading