@specs-feup/clava-misra 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CxxSources/lib.cpp +3 -0
- package/CxxSources/lib.h +8 -0
- package/CxxSources/main.cpp +40 -0
- package/README.md +32 -0
- package/TODO.md +1 -0
- package/consumer_order.txt +2 -0
- package/enum_integer_type.txt +0 -0
- package/is_temporary.txt +0 -0
- package/jest.config.js +25 -0
- package/omp.txt +0 -0
- package/package.json +23 -0
- package/src/foo.ts +6 -0
- package/src/main.ts +36 -0
- package/src/misra/MISRAAnalyser.ts +43 -0
- package/src/misra/MISRAPass.ts +85 -0
- package/src/misra/MISRAPassResult.ts +20 -0
- package/src/misra/MISRAReporter.ts +57 -0
- package/src/misra/passes/S10_EssentialTypePass.ts +395 -0
- package/src/misra/passes/S12_ExpressionPass.ts +86 -0
- package/src/misra/passes/S13_SideEffectPass.ts +121 -0
- package/src/misra/passes/S15_ControlFlowPass.ts +108 -0
- package/src/misra/passes/S16_SwitchStatementPass.ts +168 -0
- package/src/misra/passes/S17_FunctionPass.ts +43 -0
- package/src/misra/passes/S18_PointersArraysPass.ts +126 -0
- package/src/misra/passes/S19_OverlappingStoragePass.ts +27 -0
- package/src/misra/passes/S21_StandardLibPass.ts +92 -0
- package/src/misra/passes/S3_CommentPass.ts +40 -0
- package/src/misra/passes/S5_IdentifierPass.ts +66 -0
- package/src/misra/passes/S6_TypePass.ts +32 -0
- package/src/misra/passes/S7_LiteralsConstantsPass.ts +80 -0
- package/src/misra/passes/S8_DeclDefPass.ts +138 -0
- package/src/misra/sections/Section10_EssentialTypeModel.ts +377 -0
- package/src/misra/sections/Section11_PointerTypeConversions.ts +103 -0
- package/src/misra/sections/Section12_Expressions.ts +80 -0
- package/src/misra/sections/Section13_SideEffects.ts +100 -0
- package/src/misra/sections/Section14_ControlStmtExprs.ts +27 -0
- package/src/misra/sections/Section15_ControlFlow.ts +114 -0
- package/src/misra/sections/Section16_SwitchStatements.ts +154 -0
- package/src/misra/sections/Section17_Functions.ts +33 -0
- package/src/misra/sections/Section18_PointersAndArrays.ts +108 -0
- package/src/misra/sections/Section19_OverlappingStorage.ts +18 -0
- package/src/misra/sections/Section20_PreprocessingDirectives.ts +22 -0
- package/src/misra/sections/Section21_StandardLibraries.ts +65 -0
- package/src/misra/sections/Section2_UnusedCode.ts +47 -0
- package/src/misra/sections/Section3_Comments.ts +23 -0
- package/src/misra/sections/Section5_Identifiers.ts +149 -0
- package/src/misra/sections/Section6_Types.ts +26 -0
- package/src/misra/sections/Section7_LiteralsConstants.ts +76 -0
- package/src/misra/sections/Section8_DeclarationsDefinitions.ts +133 -0
- package/src/misra/tests/S10_EssentialTypes.test.ts +253 -0
- package/src/misra/tests/S12_Expressions.test.ts +43 -0
- package/src/misra/tests/S13_SideEffects.test.ts +77 -0
- package/src/misra/tests/S15_ControlFlow.test.ts +144 -0
- package/src/misra/tests/S16_SwitchStatements.test.ts +164 -0
- package/src/misra/tests/S17_Functions.test.ts +46 -0
- package/src/misra/tests/S18_PointersArrays.test.ts +167 -0
- package/src/misra/tests/S19_OverlappingStorage.test.ts +38 -0
- package/src/misra/tests/S3_Comments.test.ts +36 -0
- package/src/misra/tests/S6_Types.test.ts +36 -0
- package/src/misra/tests/S7_LiteralsConstants.test.ts +48 -0
- package/src/misra/tests/utils.ts +47 -0
- package/tsconfig.jest.json +5 -0
- package/tsconfig.json +18 -0
- package/typedoc.config.js +6 -0
- package/types_with_templates.txt +0 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
|
2
|
+
import MISRAPass from "../MISRAPass.js"
|
3
|
+
import { PreprocessingReqs } from "../MISRAReporter.js";
|
4
|
+
import { BuiltinType, Field, IntLiteral, Joinpoint } from "@specs-feup/clava/api/Joinpoints.js";
|
5
|
+
|
6
|
+
export default class S6_TypePass extends MISRAPass {
|
7
|
+
protected _preprocessingReqs: PreprocessingReqs[] = [];
|
8
|
+
|
9
|
+
initRuleMapper(): void {
|
10
|
+
this._ruleMapper = new Map([
|
11
|
+
[2, this.r6_2_noSingleBitSignedFields.bind(this)],
|
12
|
+
]);
|
13
|
+
}
|
14
|
+
|
15
|
+
matchJoinpoint($jp: LaraJoinPoint): boolean {
|
16
|
+
return $jp instanceof Field;
|
17
|
+
}
|
18
|
+
|
19
|
+
private r6_2_noSingleBitSignedFields($startNode: Joinpoint) {
|
20
|
+
if (!($startNode instanceof Field)) return;
|
21
|
+
|
22
|
+
if ($startNode.children.length > 0 && $startNode.name) {
|
23
|
+
const width = new Number(($startNode.children[0].children[0] as IntLiteral).value);
|
24
|
+
if (width == 1 && ($startNode.type as BuiltinType).isSigned) {
|
25
|
+
this.logMISRAError(`Single-bit named bit field ${$startNode.name} must not have a signed type.`);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
protected _name: string = "Types";
|
31
|
+
|
32
|
+
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
|
2
|
+
import MISRAPass from "../MISRAPass.js";
|
3
|
+
import { PreprocessingReqs } from "../MISRAReporter.js";
|
4
|
+
import { ArrayAccess, BinaryOp, Call, FunctionJp, IntLiteral, Joinpoint, PointerType, QualType, ReturnStmt, Type, Vardecl, Varref } from "@specs-feup/clava/api/Joinpoints.js";
|
5
|
+
|
6
|
+
export default class S7_LiteralsConstantsPass extends MISRAPass {
|
7
|
+
protected _preprocessingReqs: PreprocessingReqs[] = [];
|
8
|
+
|
9
|
+
initRuleMapper(): void {
|
10
|
+
this._ruleMapper = new Map([
|
11
|
+
[1, this.r7_1_noOctalConstants.bind(this)],
|
12
|
+
[3, this.r7_3_noLowercaseLSuffix.bind(this)],
|
13
|
+
[4, this.r7_4_constStringLiterals.bind(this)]
|
14
|
+
]);
|
15
|
+
}
|
16
|
+
|
17
|
+
matchJoinpoint($jp: LaraJoinPoint): boolean {
|
18
|
+
return $jp instanceof IntLiteral || $jp instanceof Vardecl || $jp instanceof BinaryOp || $jp instanceof ReturnStmt || $jp instanceof Call;
|
19
|
+
}
|
20
|
+
|
21
|
+
private r7_1_noOctalConstants($startNode: Joinpoint) {
|
22
|
+
if (!($startNode instanceof IntLiteral)) return;
|
23
|
+
|
24
|
+
if ($startNode.code.match(/0[0-9]+/g)) {
|
25
|
+
this.logMISRAError(`The octal constant ${$startNode.code} was used. Its decimal value is ${$startNode.value}`);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
private r7_3_noLowercaseLSuffix($startNode: Joinpoint) {
|
30
|
+
if (!($startNode instanceof IntLiteral)) return;
|
31
|
+
|
32
|
+
if ($startNode.code.includes('l')) {
|
33
|
+
this.logMISRAError(`A lowercase 'l' was used as a suffix in ${$startNode.code}.`);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
private static checkPointerConst(type: Type) {
|
38
|
+
if (type instanceof PointerType) {
|
39
|
+
return type.pointee.constant;
|
40
|
+
}
|
41
|
+
else if (type instanceof QualType && type.unqualifiedType instanceof PointerType) {
|
42
|
+
return type.unqualifiedType.pointee.constant;
|
43
|
+
}
|
44
|
+
else return undefined;
|
45
|
+
}
|
46
|
+
|
47
|
+
private r7_4_constStringLiterals($startNode: Joinpoint) {
|
48
|
+
if ($startNode instanceof Vardecl) {
|
49
|
+
if (!$startNode.type.isPointer) return;
|
50
|
+
|
51
|
+
if ($startNode.children.length > 0 && $startNode.children[0].joinPointType === "literal"
|
52
|
+
&& !S7_LiteralsConstantsPass.checkPointerConst($startNode.type)) {
|
53
|
+
this.logMISRAError(`String literal assigned to non-const qualified variable ${$startNode.name}`);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
else if ($startNode instanceof BinaryOp && $startNode.isAssignment) {
|
57
|
+
if ($startNode.right.joinPointType === "literal" && !S7_LiteralsConstantsPass.checkPointerConst($startNode.left.type)) {
|
58
|
+
this.logMISRAError(`String literal assigned to non-const qualified variable ${($startNode.left as Varref | ArrayAccess).name}`);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
else if ($startNode instanceof ReturnStmt) {
|
62
|
+
const ancestor = $startNode.getAncestor("function") as FunctionJp;
|
63
|
+
const retType = ancestor.functionType.returnType;
|
64
|
+
if ($startNode.returnExpr?.joinPointType === "literal" && !S7_LiteralsConstantsPass.checkPointerConst(retType)) {
|
65
|
+
this.logMISRAError(`String literal returned in non-const qualified return value for function ${ancestor.name}`);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
else if ($startNode instanceof Call) {
|
69
|
+
const paramTypes = $startNode.function.functionType.paramTypes;
|
70
|
+
for (let i = 1; i < $startNode.children.length; i++) {
|
71
|
+
if ($startNode.children[i].joinPointType === "literal" && !S7_LiteralsConstantsPass.checkPointerConst(paramTypes[i-1])) {
|
72
|
+
this.logMISRAError(`String literal passed as non-const qualified parameter in call of ${$startNode.function.name}`);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
protected _name: string = "Literals and constants";
|
79
|
+
|
80
|
+
}
|
@@ -0,0 +1,138 @@
|
|
1
|
+
import { LaraJoinPoint } from "@specs-feup/lara/api/LaraJoinPoint.js";
|
2
|
+
import MISRAPass from "../MISRAPass.js";
|
3
|
+
import { PreprocessingReqs } from "../MISRAReporter.js";
|
4
|
+
import { EnumDecl, FunctionJp, IntLiteral, Joinpoint, Param, StorageClass, Vardecl } from "@specs-feup/clava/api/Joinpoints.js";
|
5
|
+
import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
|
6
|
+
|
7
|
+
export default class S8_DeclDefPass extends MISRAPass {
|
8
|
+
protected _preprocessingReqs: PreprocessingReqs[] = [];
|
9
|
+
initRuleMapper(): void {
|
10
|
+
this._ruleMapper = new Map([
|
11
|
+
[2, this.r8_2_functionPrototype.bind(this)],
|
12
|
+
[3, this.r8_3_compatibleDefinitions.bind(this)],
|
13
|
+
[7, this.r8_7_noUnnecessaryExternalLinkage.bind(this)],
|
14
|
+
[10, this.r8_10_onlyStaticInline.bind(this)],
|
15
|
+
[11, this.r8_11_externArrayExplicitSize.bind(this)],
|
16
|
+
[12, this.r8_12_implicitExplicitEnumMatching.bind(this)]
|
17
|
+
]);
|
18
|
+
}
|
19
|
+
matchJoinpoint($jp: LaraJoinPoint): boolean {
|
20
|
+
return $jp instanceof Param || $jp instanceof FunctionJp || $jp instanceof Vardecl || $jp instanceof EnumDecl;
|
21
|
+
}
|
22
|
+
|
23
|
+
private r8_2_functionPrototype($startNode: Joinpoint) { //needs to apply to function pointers, void info lost
|
24
|
+
if (!($startNode instanceof Param)) return;
|
25
|
+
|
26
|
+
if (!$startNode.name) this.logMISRAError(`Parameter of type ${$startNode.type.code} lacks a name.`);
|
27
|
+
}
|
28
|
+
|
29
|
+
private r8_3_compatibleDefinitions($startNode: Joinpoint) { //what if no impl?
|
30
|
+
if (!($startNode instanceof FunctionJp && $startNode.isImplementation)) return;
|
31
|
+
|
32
|
+
$startNode.declarationJps.forEach(decl => {
|
33
|
+
for (let i = 0; i < $startNode.params.length; i++) {
|
34
|
+
if ($startNode.paramNames[i] !== decl.paramNames[i]) {
|
35
|
+
this.logMISRAError(`Mismatch in name of parameters with declaration on ${decl.filename}@${decl.line}:${decl.column}.`);
|
36
|
+
}
|
37
|
+
if ($startNode.params[i].type.code !== decl.params[i].type.code) {
|
38
|
+
this.logMISRAError(`Mismatch in parameter types with declaration on ${decl.filename}@${decl.line}:${decl.column}`);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}, this);
|
42
|
+
}
|
43
|
+
|
44
|
+
private static hasExternalLinkage(jp: FunctionJp | Vardecl) {
|
45
|
+
return jp.storageClass !== StorageClass.STATIC && jp.storageClass !== StorageClass.EXTERN;
|
46
|
+
}
|
47
|
+
|
48
|
+
private r8_7_noUnnecessaryExternalLinkage($startNode: Joinpoint) { //finish
|
49
|
+
/*const globals = new Map();
|
50
|
+
|
51
|
+
Query.searchFrom($startNode, FileJp).get().forEach(file => {
|
52
|
+
file.children.forEach(jp => {
|
53
|
+
if (jp instanceof FunctionJp && jp.name !== "main" && Section8_DeclarationsDefinitions.hasExternalLinkage(jp)) {
|
54
|
+
let hasExternals = false;
|
55
|
+
jp.calls.forEach(call => {
|
56
|
+
if (call.filename !== jp.filename) {
|
57
|
+
hasExternals = true;
|
58
|
+
}
|
59
|
+
}, this);
|
60
|
+
if (!hasExternals) {
|
61
|
+
this.logMISRAError(jp, `Function ${jp.name} has external linkage but it is only referenced in its file.`);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
if (jp instanceof DeclStmt) {
|
66
|
+
jp.decls.filter(decl => decl instanceof Vardecl && Section8_DeclarationsDefinitions.hasExternalLinkage(decl)).forEach(decl => {
|
67
|
+
//globals.push({decl: decl as Vardecl, file: decl.filename});
|
68
|
+
globals.set(decl.astId, {decl: decl as Vardecl, file: decl.filename});
|
69
|
+
}, this);
|
70
|
+
}
|
71
|
+
});
|
72
|
+
}, this);
|
73
|
+
|
74
|
+
Query.searchFrom($startNode, Varref).get().forEach(ref => {
|
75
|
+
if (globals.has(ref.astId)) {
|
76
|
+
const declFile = globals.get(ref.astId).file;
|
77
|
+
if (declFile !== ref.filename) {
|
78
|
+
globals.delete(ref.astId);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}, this);
|
82
|
+
|
83
|
+
globals.forEach((v, k, m) => {
|
84
|
+
this.logMISRAError(v.decl, `Variable ${v.decl.name} is declared with external linkage but is not referenced outside its file.`);
|
85
|
+
}, this);
|
86
|
+
*/}
|
87
|
+
|
88
|
+
private r8_10_onlyStaticInline($startNode: Joinpoint) {
|
89
|
+
if (!($startNode instanceof FunctionJp && $startNode.isInline)) return;
|
90
|
+
|
91
|
+
if ($startNode.storageClass !== StorageClass.STATIC) {
|
92
|
+
this.logMISRAError("Inline functions must always be declared static.");
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
private r8_11_externArrayExplicitSize($startNode: Joinpoint) {
|
97
|
+
if (!($startNode instanceof Vardecl && $startNode.storageClass === StorageClass.EXTERN)) return;
|
98
|
+
|
99
|
+
if ($startNode.type.isArray && $startNode.type.arraySize === -1) {
|
100
|
+
this.logMISRAError(`Size of external array ${$startNode.name} is not explicit.`)
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
private setEnumMap(map: Map<number, boolean>, newValue: number, isExplicit: boolean, jp: EnumDecl) {
|
105
|
+
if (map.has(newValue) && !(isExplicit && map.get(newValue))) {
|
106
|
+
this.logMISRAError(`An implicitly numbered identifier in enum ${jp.name} shares a value with another identifier.`, new Fix(jp, jp => {
|
107
|
+
|
108
|
+
}));
|
109
|
+
}
|
110
|
+
map.set(newValue, isExplicit);
|
111
|
+
}
|
112
|
+
|
113
|
+
private r8_12_implicitExplicitEnumMatching($startNode: Joinpoint) {
|
114
|
+
if (!($startNode instanceof EnumDecl)) return;
|
115
|
+
|
116
|
+
const map = new Map();
|
117
|
+
|
118
|
+
let index = 0;
|
119
|
+
for (const enumerator of $startNode.enumerators) {
|
120
|
+
if (enumerator.children.length > 0) {
|
121
|
+
if (enumerator.children[0].children[0] instanceof IntLiteral) {
|
122
|
+
index = Number(enumerator.children[0].children[0].value);
|
123
|
+
this.setEnumMap(map, index, true, $startNode);
|
124
|
+
}
|
125
|
+
else {
|
126
|
+
console.log(`Warning! Could not analyse the entirety of enum ${$startNode.name} due to non-constant expressions`);
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
else {
|
131
|
+
this.setEnumMap(map, index, false, $startNode);
|
132
|
+
}
|
133
|
+
index++;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
protected _name: string = "Declarations and definitions";
|
138
|
+
}
|
@@ -0,0 +1,377 @@
|
|
1
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
2
|
+
import { Program, FileJp, TernaryOp, UnaryOp, BinaryOp, Joinpoint, Cast, BuiltinType, Type, Expression, IntLiteral, EnumType, QualType, ReturnStmt, FunctionJp, Call, Op, ParenExpr, Varref } from "@specs-feup/clava/api/Joinpoints.js";
|
3
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
4
|
+
import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
|
5
|
+
import ClavaJoinPoints from "@specs-feup/clava/api/clava/ClavaJoinPoints.js";
|
6
|
+
|
7
|
+
export enum EssentialTypes {
|
8
|
+
UNSIGNED = "unsigned",
|
9
|
+
CHAR = "char",
|
10
|
+
SIGNED = "signed",
|
11
|
+
ENUM = "enum",
|
12
|
+
FLOAT = "float",
|
13
|
+
BOOL = "bool",
|
14
|
+
UNKOWN = "unkown"
|
15
|
+
};
|
16
|
+
|
17
|
+
export default class Section10_EssentialTypeModel extends MISRAAnalyser {
|
18
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
19
|
+
|
20
|
+
constructor(rules: number[]) {
|
21
|
+
super(rules);
|
22
|
+
this.ruleMapper = new Map([
|
23
|
+
[1, this.r10_1_appropriateEssentialOperands.bind(this)],
|
24
|
+
[2, this.r10_2_appropriateCharOperands.bind(this)],
|
25
|
+
[3, this.r10_3_noInvalidAssignments.bind(this)],
|
26
|
+
[5, this.r10_5_noInvalidCasts.bind(this)],
|
27
|
+
[6, this.r10_6_noWiderCompositeExprAssignments.bind(this)],
|
28
|
+
[8, this.r10_8_noWiderCompositeCasts.bind(this)]
|
29
|
+
]);
|
30
|
+
}
|
31
|
+
|
32
|
+
static getEssentialType(bType: Type): EssentialTypes {
|
33
|
+
let type = bType.desugarAll;
|
34
|
+
while (type instanceof QualType) {
|
35
|
+
type = type.unqualifiedType.desugarAll;
|
36
|
+
}
|
37
|
+
|
38
|
+
if (type instanceof BuiltinType) {
|
39
|
+
if (type.builtinKind === "Bool") {
|
40
|
+
return EssentialTypes.BOOL;
|
41
|
+
}
|
42
|
+
else if (type.builtinKind === "Char_S") {
|
43
|
+
return EssentialTypes.CHAR;
|
44
|
+
}
|
45
|
+
else if (type.isInteger && type.isSigned) {
|
46
|
+
return EssentialTypes.SIGNED;
|
47
|
+
}
|
48
|
+
else if (type.isInteger && !type.isSigned) {
|
49
|
+
return EssentialTypes.UNSIGNED;
|
50
|
+
}
|
51
|
+
else if (type.isFloat) {
|
52
|
+
return EssentialTypes.FLOAT;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
else if (type instanceof EnumType) {
|
56
|
+
return type.name === undefined ? EssentialTypes.SIGNED : EssentialTypes.ENUM;
|
57
|
+
}
|
58
|
+
|
59
|
+
return EssentialTypes.UNKOWN;
|
60
|
+
}
|
61
|
+
|
62
|
+
static isInteger($et: EssentialTypes): boolean {
|
63
|
+
return $et === EssentialTypes.SIGNED || $et === EssentialTypes.UNSIGNED;
|
64
|
+
}
|
65
|
+
|
66
|
+
static getExprEssentialType($expr: Expression): EssentialTypes {
|
67
|
+
if ($expr instanceof BinaryOp) {
|
68
|
+
if ($expr.kind === "add") {
|
69
|
+
if ((this.getExprEssentialType($expr.left) === EssentialTypes.CHAR && this.isInteger(this.getExprEssentialType($expr.right)))
|
70
|
+
|| (this.getExprEssentialType($expr.right) === EssentialTypes.CHAR && this.isInteger(this.getExprEssentialType($expr.left)))) {
|
71
|
+
return EssentialTypes.CHAR;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
else if ($expr.kind === "sub") {
|
75
|
+
if (this.getExprEssentialType($expr.left) === EssentialTypes.CHAR) {
|
76
|
+
if (this.getExprEssentialType($expr.right) === EssentialTypes.CHAR) {
|
77
|
+
return EssentialTypes.CHAR;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
return this.getEssentialType($expr.type);
|
83
|
+
}
|
84
|
+
|
85
|
+
private restrictOperand($expr: Expression, $restrictedType: EssentialTypes, $baseExpr: Expression, $castTo?: BuiltinType) {
|
86
|
+
const et = Section10_EssentialTypeModel.getExprEssentialType($expr);
|
87
|
+
if (et === $restrictedType) {
|
88
|
+
const fix = $castTo ? new Fix($expr, ($jp) => {
|
89
|
+
$jp.replaceWith(ClavaJoinPoints.cStyleCast($castTo, $jp as Expression));
|
90
|
+
}) : undefined;
|
91
|
+
this.logMISRAError($baseExpr, `Operand ${$expr.code} of expression ${$baseExpr.code} must not have essentially ${et} type.`, fix);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
private restrictOperandList($expr: Expression, $restrictedTypes: EssentialTypes[], $baseExpr: Expression, $castTo?: BuiltinType) {
|
96
|
+
for (const type of $restrictedTypes) {
|
97
|
+
this.restrictOperand($expr, type, $baseExpr, $castTo);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
private r10_1_appropriateEssentialOperands($startNode: Joinpoint) { //missing exception and compound operators
|
102
|
+
Query.searchFrom($startNode, Op).get().forEach(op => {
|
103
|
+
if (op instanceof TernaryOp) {
|
104
|
+
this.restrictOperandList(op.cond, [EssentialTypes.CHAR, EssentialTypes.ENUM, EssentialTypes.FLOAT, EssentialTypes.SIGNED, EssentialTypes.UNSIGNED], op);
|
105
|
+
}
|
106
|
+
else if (op instanceof BinaryOp) {
|
107
|
+
switch (op.kind) {
|
108
|
+
case "shl":
|
109
|
+
case "shr":
|
110
|
+
if (!(op.right instanceof IntLiteral)) this.restrictOperand(op.right, EssentialTypes.SIGNED, op);
|
111
|
+
case "and":
|
112
|
+
case "or":
|
113
|
+
case "x_or":
|
114
|
+
this.restrictOperand(op.left, EssentialTypes.SIGNED, op);
|
115
|
+
if (op.kind !== "shl" && op.kind !== "shr") this.restrictOperand(op.right, EssentialTypes.SIGNED, op);
|
116
|
+
case "rem":
|
117
|
+
this.restrictOperand(op.left, EssentialTypes.FLOAT, op);
|
118
|
+
this.restrictOperand(op.right, EssentialTypes.FLOAT, op);
|
119
|
+
case "mul":
|
120
|
+
case "div":
|
121
|
+
this.restrictOperand(op.left, EssentialTypes.CHAR, op);
|
122
|
+
this.restrictOperand(op.right, EssentialTypes.CHAR, op);
|
123
|
+
case "add":
|
124
|
+
case "sub":
|
125
|
+
this.restrictOperand(op.left, EssentialTypes.ENUM, op);
|
126
|
+
this.restrictOperand(op.right, EssentialTypes.ENUM, op);
|
127
|
+
case "le":
|
128
|
+
case "ge":
|
129
|
+
case "lt":
|
130
|
+
case "gt":
|
131
|
+
this.restrictOperand(op.left, EssentialTypes.BOOL, op);
|
132
|
+
this.restrictOperand(op.right, EssentialTypes.BOOL, op);
|
133
|
+
break;
|
134
|
+
case "l_and":
|
135
|
+
case "l_or":
|
136
|
+
this.restrictOperandList(op.left, [EssentialTypes.ENUM, EssentialTypes.CHAR, EssentialTypes.SIGNED, EssentialTypes.UNSIGNED, EssentialTypes.FLOAT], op);
|
137
|
+
this.restrictOperandList(op.right, [EssentialTypes.ENUM, EssentialTypes.CHAR, EssentialTypes.SIGNED, EssentialTypes.UNSIGNED, EssentialTypes.FLOAT], op);
|
138
|
+
break;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
else if (op instanceof UnaryOp) {
|
142
|
+
switch (op.kind) {
|
143
|
+
case "minus":
|
144
|
+
this.restrictOperand(op.operand, EssentialTypes.UNSIGNED, op);
|
145
|
+
case "plus":
|
146
|
+
this.restrictOperandList(op.operand, [EssentialTypes.BOOL, EssentialTypes.CHAR, EssentialTypes.ENUM], op);
|
147
|
+
break;
|
148
|
+
case "l_not":
|
149
|
+
this.restrictOperandList(op.operand, [EssentialTypes.ENUM, EssentialTypes.CHAR, EssentialTypes.SIGNED, EssentialTypes.UNSIGNED, EssentialTypes.FLOAT], op);
|
150
|
+
break;
|
151
|
+
case "not":
|
152
|
+
this.restrictOperandList(op.operand, [EssentialTypes.BOOL, EssentialTypes.CHAR, EssentialTypes.ENUM, EssentialTypes.FLOAT, EssentialTypes.SIGNED], op);
|
153
|
+
break;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}, this);
|
157
|
+
}
|
158
|
+
|
159
|
+
private r10_2_appropriateCharOperands($startNode: Joinpoint) {
|
160
|
+
Query.searchFrom($startNode, BinaryOp, {kind: /(add|sub)/}).get().forEach(bOp => {
|
161
|
+
if (bOp.kind === "add") {
|
162
|
+
if (Section10_EssentialTypeModel.getExprEssentialType(bOp.left) === EssentialTypes.CHAR && Section10_EssentialTypeModel.getExprEssentialType(bOp.right) === EssentialTypes.CHAR) {
|
163
|
+
this.logMISRAError(bOp, `Both operands of addition ${bOp.code} have essentially character type.`);
|
164
|
+
return;
|
165
|
+
}
|
166
|
+
|
167
|
+
let otherType;
|
168
|
+
if (Section10_EssentialTypeModel.getExprEssentialType(bOp.left) === EssentialTypes.CHAR) {
|
169
|
+
otherType = Section10_EssentialTypeModel.getExprEssentialType(bOp.right);
|
170
|
+
}
|
171
|
+
else if (Section10_EssentialTypeModel.getExprEssentialType(bOp.right) === EssentialTypes.CHAR) {
|
172
|
+
otherType = Section10_EssentialTypeModel.getExprEssentialType(bOp.left);
|
173
|
+
}
|
174
|
+
|
175
|
+
if (otherType && !Section10_EssentialTypeModel.isInteger(otherType)) {
|
176
|
+
this.logMISRAError(bOp, `One operand of addition ${bOp.code} has essentially character type, so the other one must have either essentially signed or unsigned type.`);
|
177
|
+
return;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
else if (bOp.kind === "sub") {
|
181
|
+
if (Section10_EssentialTypeModel.getExprEssentialType(bOp.left) === EssentialTypes.CHAR) {
|
182
|
+
const rightType = Section10_EssentialTypeModel.getExprEssentialType(bOp.right);
|
183
|
+
if (!([EssentialTypes.CHAR, EssentialTypes.SIGNED, EssentialTypes.UNKOWN].some(et => et === rightType))) {
|
184
|
+
this.logMISRAError(bOp, `Left operand of subtraction ${bOp.code} has essentially character type, so the RHS must be essentially signed, unsigned, or char.`);
|
185
|
+
return;
|
186
|
+
}
|
187
|
+
}
|
188
|
+
else if (Section10_EssentialTypeModel.getExprEssentialType(bOp.right) === EssentialTypes.CHAR) {
|
189
|
+
this.logMISRAError(bOp, `Right operand of subtraction ${bOp.code} can only be of essentially character type if the LHS is too.`);
|
190
|
+
return;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}, this);
|
194
|
+
}
|
195
|
+
|
196
|
+
private r10_3_noInvalidAssignments($startNode: Joinpoint) { //not working for decls
|
197
|
+
Query.searchFrom($startNode, BinaryOp, {kind: "assign"}).get().forEach(bOp => {
|
198
|
+
if (Section10_EssentialTypeModel.getEssentialType(bOp.left.type) !== Section10_EssentialTypeModel.getEssentialType(bOp.right.type)) {
|
199
|
+
this.logMISRAError(bOp, `Value ${bOp.right.code} cannot be assigned to ${bOp.left.code}, since it has a different essential type category.`);
|
200
|
+
}
|
201
|
+
else if (bOp.left.bitWidth < bOp.right.bitWidth) {
|
202
|
+
this.logMISRAError(bOp, `Value ${bOp.right.code} cannot be assigned to ${bOp.left.code} since it has a narrower type.`);
|
203
|
+
}
|
204
|
+
}, this);
|
205
|
+
Query.searchFrom($startNode, ReturnStmt).get().forEach(ret => {
|
206
|
+
const fun = ret.getAncestor("function") as FunctionJp;
|
207
|
+
console.log(ret.returnExpr.code, ret.returnExpr.bitWidth);
|
208
|
+
console.log(fun.bitWidth);
|
209
|
+
if (Section10_EssentialTypeModel.getEssentialType(ret.returnExpr.type) !== Section10_EssentialTypeModel.getEssentialType(fun.returnType)) {
|
210
|
+
this.logMISRAError(ret, `Value ${ret.returnExpr.code} cannot be returned by ${fun.signature}, since it has a different essential type category.`);
|
211
|
+
}
|
212
|
+
else if (fun.bitWidth < ret.returnExpr.bitWidth) {
|
213
|
+
this.logMISRAError(ret, `Value ${ret.returnExpr.code} cannot be returned by ${fun.signature} since it has a narrower type.`);
|
214
|
+
}
|
215
|
+
}, this);
|
216
|
+
Query.searchFrom($startNode, Call).get().forEach(call => {
|
217
|
+
const funParams = call.directCallee.params;
|
218
|
+
for (let i = 0; i < funParams.length; i++) {
|
219
|
+
if (Section10_EssentialTypeModel.getEssentialType(funParams[i].type) !== Section10_EssentialTypeModel.getEssentialType(call.argList[0].type)) {
|
220
|
+
this.logMISRAError(call, `Value ${call.argList[i].code} cannot be assigned to parameter ${funParams[i].code}, since it has a different essential type category.`);
|
221
|
+
}
|
222
|
+
else if (funParams[i].bitWidth < call.argList[i].bitWidth) {
|
223
|
+
this.logMISRAError(call, `Value ${call.argList[i].code} cannot be assigned to parameter ${funParams[i].code}, since it has a narrower type.`);
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}, this);
|
227
|
+
}
|
228
|
+
|
229
|
+
private static checkBoolSource(subExpr: Expression) {
|
230
|
+
if (subExpr.type.desugarAll instanceof BuiltinType && subExpr.type.desugarAll.builtinKind === "Bool") {
|
231
|
+
return true;
|
232
|
+
}
|
233
|
+
else if (subExpr instanceof IntLiteral && (Number(subExpr.value) === 0 || Number(subExpr.value) === 1)) {
|
234
|
+
return true;
|
235
|
+
}
|
236
|
+
else return false;
|
237
|
+
}
|
238
|
+
|
239
|
+
private r10_5_noInvalidCasts($startNode: Joinpoint) {//chars, anonymous enums
|
240
|
+
Query.searchFrom($startNode, Cast).get().forEach(cast => {
|
241
|
+
const fromType = cast.fromType.desugarAll;
|
242
|
+
const toType = cast.toType.desugarAll;
|
243
|
+
|
244
|
+
if (toType instanceof BuiltinType) {
|
245
|
+
console.log(toType.builtinKind)
|
246
|
+
}
|
247
|
+
|
248
|
+
if (toType instanceof BuiltinType && toType.builtinKind === "Bool" && !Section10_EssentialTypeModel.checkBoolSource(cast.subExpr)) {
|
249
|
+
this.logMISRAError(cast, "Only essentially boolean values, or the integer constants 0 or 1, may be cast to an essentially boolean type.");
|
250
|
+
}
|
251
|
+
else if (toType instanceof EnumType && !(fromType instanceof EnumType && toType.name === fromType.name)) {
|
252
|
+
this.logMISRAError(cast, "Only essentially enum values of the same enum may be cast to an essentially enum type.");
|
253
|
+
}
|
254
|
+
else if (toType instanceof BuiltinType && toType.builtinKind === "Int" && toType.isSigned && fromType instanceof BuiltinType && fromType.builtinKind === "Bool") {
|
255
|
+
this.logMISRAError(cast, "Essentially boolean values should not be cast to an essentially signed type.");
|
256
|
+
}
|
257
|
+
else if (toType instanceof BuiltinType && toType.builtinKind === "Int" && !toType.isSigned && fromType instanceof BuiltinType && fromType.builtinKind === "Bool") {
|
258
|
+
this.logMISRAError(cast, "Essentially boolean values should not be cast to an essentially unsigned type.");
|
259
|
+
}
|
260
|
+
else if (toType instanceof BuiltinType && toType.isFloat && fromType instanceof BuiltinType && fromType.builtinKind === "Bool") {
|
261
|
+
this.logMISRAError(cast, "Essentially boolean values should not be cast to an essentially floating type.");
|
262
|
+
}
|
263
|
+
}, this);
|
264
|
+
}
|
265
|
+
|
266
|
+
private static isCompositeBinaryExpr($op: BinaryOp) {
|
267
|
+
return /(add)|(sub)|(mul)|(div)|(rem)|(shl)|(shr)|(l_and)|(l_or)|(xor)/.test($op.kind);
|
268
|
+
}
|
269
|
+
|
270
|
+
private static isCompositeExpr($expr: Expression): Expression | undefined {
|
271
|
+
if ($expr instanceof ParenExpr) {
|
272
|
+
return this.isCompositeExpr($expr.subExpr);
|
273
|
+
}
|
274
|
+
else if ($expr instanceof BinaryOp) {
|
275
|
+
if (this.isCompositeBinaryExpr($expr)) {
|
276
|
+
return $expr;
|
277
|
+
}
|
278
|
+
}
|
279
|
+
else if ($expr instanceof TernaryOp) {
|
280
|
+
if (this.isCompositeExpr($expr.trueExpr) || this.isCompositeExpr($expr.falseExpr)) {
|
281
|
+
return $expr;
|
282
|
+
}
|
283
|
+
}
|
284
|
+
else return undefined;
|
285
|
+
}
|
286
|
+
|
287
|
+
private static compositeExprWidth($op: Expression): number {
|
288
|
+
if ($op instanceof BinaryOp && Section10_EssentialTypeModel.isCompositeBinaryExpr($op)) {
|
289
|
+
const leftW = Section10_EssentialTypeModel.compositeExprWidth($op.left);
|
290
|
+
const rightW = Section10_EssentialTypeModel.compositeExprWidth($op.right);
|
291
|
+
return Math.max(leftW, rightW);
|
292
|
+
}
|
293
|
+
else if ($op instanceof TernaryOp) {
|
294
|
+
const secondW = Section10_EssentialTypeModel.compositeExprWidth($op.trueExpr);
|
295
|
+
const thirdW = Section10_EssentialTypeModel.compositeExprWidth($op.falseExpr);
|
296
|
+
return Math.min(secondW, thirdW);
|
297
|
+
}
|
298
|
+
else if ($op instanceof ParenExpr) {
|
299
|
+
return this.compositeExprWidth($op.subExpr);
|
300
|
+
}
|
301
|
+
else return $op.bitWidth;
|
302
|
+
}
|
303
|
+
|
304
|
+
private static transformBinaryOp($expr: BinaryOp, $type: Type) {
|
305
|
+
$expr.left.replaceWith(ClavaJoinPoints.cStyleCast($type, $expr.left));
|
306
|
+
}
|
307
|
+
|
308
|
+
private static transformTernaryOp($expr: TernaryOp, $type: Type, $bitWidth: number) {
|
309
|
+
const trueExpr = this.isCompositeExpr($expr.trueExpr);
|
310
|
+
const falseExpr = this.isCompositeExpr($expr.falseExpr);
|
311
|
+
if (trueExpr && this.compositeExprWidth(trueExpr) < $bitWidth) {
|
312
|
+
if (trueExpr instanceof TernaryOp) this.transformTernaryOp(trueExpr, $type, $bitWidth);
|
313
|
+
else if (trueExpr instanceof BinaryOp) this.transformBinaryOp(trueExpr, $type);
|
314
|
+
}
|
315
|
+
|
316
|
+
if (falseExpr && this.compositeExprWidth(falseExpr) < $type.bitWidth) {
|
317
|
+
if (falseExpr instanceof TernaryOp) this.transformTernaryOp(falseExpr, $type, $bitWidth);
|
318
|
+
else if (falseExpr instanceof BinaryOp) this.transformBinaryOp(falseExpr, $type);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
322
|
+
private r10_6_noWiderCompositeExprAssignments($startNode: Joinpoint) { //unfinished for rets and params
|
323
|
+
Query.searchFrom($startNode, Expression).get().forEach(expr => {
|
324
|
+
const compositeExpr = Section10_EssentialTypeModel.isCompositeExpr(expr);
|
325
|
+
if (compositeExpr) {
|
326
|
+
const parent = expr.parent;
|
327
|
+
if (parent instanceof BinaryOp && parent.kind === "assign") {
|
328
|
+
if (Section10_EssentialTypeModel.compositeExprWidth(compositeExpr) < parent.left.bitWidth) {
|
329
|
+
this.logMISRAError(compositeExpr, "A composite expression must not be assigned to a value with wider type.", new Fix(compositeExpr, op => {
|
330
|
+
if (op instanceof BinaryOp) {
|
331
|
+
Section10_EssentialTypeModel.transformBinaryOp(op, op.parent.type);
|
332
|
+
}
|
333
|
+
else if (op instanceof TernaryOp) {
|
334
|
+
op.replaceWith(ClavaJoinPoints.cStyleCast(op.parent.type, op));
|
335
|
+
}
|
336
|
+
}));
|
337
|
+
}
|
338
|
+
}
|
339
|
+
}
|
340
|
+
}, this);
|
341
|
+
/*Query.searchFrom($startNode, Op, {kind: /(add)|(sub)|(mul)|(div)|(rem)|(shl)|(shr)|(l_and)|(l_or)|(xor)/}).get().forEach(op => {
|
342
|
+
const parent = op.parent;
|
343
|
+
if (parent instanceof BinaryOp && parent.kind === "assign") {
|
344
|
+
if (Section10_EssentialTypeModel.compositeExprWidth(op) < parent.left.bitWidth) {
|
345
|
+
this.logMISRAError(op, "A composite expression must not be assigned to a value with wider type.", new Fix(op, op => {
|
346
|
+
const opJp = op as BinaryOp;
|
347
|
+
opJp.left.replaceWith(ClavaJoinPoints.cStyleCast(opJp.parent.type, opJp.left));
|
348
|
+
}));
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}, this);*/
|
352
|
+
}
|
353
|
+
|
354
|
+
private r10_8_noWiderCompositeCasts($startNode: Joinpoint) {
|
355
|
+
Query.searchFrom($startNode, Cast).get().forEach(cast => {
|
356
|
+
const compositeExpr = Section10_EssentialTypeModel.isCompositeExpr(cast.subExpr);
|
357
|
+
if (compositeExpr) {
|
358
|
+
if (Section10_EssentialTypeModel.getEssentialType(cast.fromType) !== Section10_EssentialTypeModel.getEssentialType(cast.toType)) {
|
359
|
+
this.logMISRAError(cast, `Composite expression ${cast.subExpr.code} cannot be cast to ${cast.toType.code}, since it has a different essential type category.`);
|
360
|
+
}
|
361
|
+
else if (cast.bitWidth > Section10_EssentialTypeModel.compositeExprWidth(compositeExpr)) {
|
362
|
+
this.logMISRAError(compositeExpr, `Composite expression ${cast.subExpr.code} cannot be cast to ${cast.toType.code} since it is a wider type.`, new Fix(cast, cast => {
|
363
|
+
const castJp = cast as Cast;
|
364
|
+
const compositeExpr = Section10_EssentialTypeModel.isCompositeExpr(castJp.subExpr);
|
365
|
+
if (compositeExpr instanceof BinaryOp) {
|
366
|
+
compositeExpr.left.replaceWith(ClavaJoinPoints.cStyleCast(cast.type, compositeExpr.left));
|
367
|
+
cast.replaceWith(compositeExpr);
|
368
|
+
}
|
369
|
+
else if (compositeExpr instanceof TernaryOp) {
|
370
|
+
Section10_EssentialTypeModel.transformTernaryOp(compositeExpr, cast.type, cast.bitWidth);
|
371
|
+
}
|
372
|
+
}));
|
373
|
+
}
|
374
|
+
}
|
375
|
+
});
|
376
|
+
}
|
377
|
+
}
|