@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,108 @@
|
|
1
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
2
|
+
import { Program, FileJp, Joinpoint, BinaryOp, Field, Param, Vardecl, FunctionJp, Type, PointerType, QualType, FunctionType } from "@specs-feup/clava/api/Joinpoints.js";
|
3
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
4
|
+
|
5
|
+
export default class Section18_PointersAndArrays extends MISRAAnalyser {
|
6
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
7
|
+
|
8
|
+
constructor(rules: number[]) {
|
9
|
+
super(rules);
|
10
|
+
this.ruleMapper = new Map([
|
11
|
+
[4, this.r18_4_noPointerArithmetic.bind(this)],
|
12
|
+
[5, this.r18_5_noExcessivePointerNesting.bind(this)],
|
13
|
+
[7, this.r18_7_noFlexibleArrayMembers.bind(this)],
|
14
|
+
[8, this.r18_8_noVariableLengthArrays.bind(this)]
|
15
|
+
]);
|
16
|
+
}
|
17
|
+
|
18
|
+
private r18_4_noPointerArithmetic($startNode: Joinpoint) {
|
19
|
+
for (const bOp of Query.searchFrom($startNode, BinaryOp, {operator: /(\+=)|(-=)|(\+)|(-)/})) {
|
20
|
+
if (!bOp.type.isPointer) continue;
|
21
|
+
this.logMISRAError(bOp, "Pointer arithmetic is not allowed. The only exception is if two pointers to elements of the same array are subtracted.")
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
private static getDepth(type: Type) {
|
26
|
+
let depth = 0;
|
27
|
+
let curr = type.desugarAll;
|
28
|
+
while (curr.isPointer === true) {
|
29
|
+
depth++;
|
30
|
+
|
31
|
+
if(curr instanceof PointerType) {
|
32
|
+
curr = curr.pointee.desugarAll;
|
33
|
+
} else if(curr instanceof QualType) {
|
34
|
+
curr = (curr.unqualifiedType as PointerType).pointee.desugarAll;
|
35
|
+
} else {
|
36
|
+
throw new Error(`Not supported for type ${curr.joinPointType}.`);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
return depth;
|
41
|
+
}
|
42
|
+
|
43
|
+
private static getUnderlyingType(type: Type) {
|
44
|
+
let curr = type.desugarAll;
|
45
|
+
while (curr.isPointer === true) {
|
46
|
+
|
47
|
+
if(curr instanceof PointerType) {
|
48
|
+
curr = curr.pointee.desugarAll;
|
49
|
+
} else if(curr instanceof QualType) {
|
50
|
+
curr = (curr.unqualifiedType as PointerType).pointee.desugarAll;
|
51
|
+
} else {
|
52
|
+
throw new Error(`Not supported for type ${curr.joinPointType}.`);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
return curr;
|
57
|
+
}
|
58
|
+
|
59
|
+
private r18_5_noExcessivePointerNesting($startNode: Joinpoint) { //must apply to fields as well
|
60
|
+
Query.searchFrom($startNode, Vardecl).get().forEach(decl => {
|
61
|
+
if (decl instanceof Param) return;
|
62
|
+
const depth = Section18_PointersAndArrays.getDepth(decl.type);
|
63
|
+
const underlyingType = Section18_PointersAndArrays.getUnderlyingType(decl.type);
|
64
|
+
if (underlyingType instanceof FunctionType) {
|
65
|
+
const retDepth = Section18_PointersAndArrays.getDepth(underlyingType.returnType);
|
66
|
+
const paramDepths = underlyingType.paramTypes.map(type => Section18_PointersAndArrays.getDepth(type)).filter(d => d > 2);
|
67
|
+
if (retDepth > 2) {
|
68
|
+
this.logMISRAError(decl, `Return type of function pointer ${decl.code} has more than two levels of indirection.`);
|
69
|
+
}
|
70
|
+
if (paramDepths.length > 0) {
|
71
|
+
this.logMISRAError(decl, `One or more parameters of function pointer ${decl.code} have more than two levels of indirection.`);
|
72
|
+
}
|
73
|
+
}
|
74
|
+
if (depth > 2) {
|
75
|
+
this.logMISRAError(decl, `Type ${decl.type.code} has more than two levels of indirection.`)
|
76
|
+
}
|
77
|
+
}, this);
|
78
|
+
|
79
|
+
Query.searchFrom($startNode, FunctionJp).get().forEach(fun => {
|
80
|
+
const retDepth = Section18_PointersAndArrays.getDepth(fun.functionType.returnType);
|
81
|
+
const paramDepths = fun.functionType.paramTypes.map(type => Section18_PointersAndArrays.getDepth(type)).filter(d => d > 2);
|
82
|
+
if (retDepth > 2) {
|
83
|
+
this.logMISRAError(fun, `Return type of function ${fun.signature} has more than two levels of indirection.`);
|
84
|
+
}
|
85
|
+
if (paramDepths.length > 0) {
|
86
|
+
this.logMISRAError(fun, `One or more parameters of function ${fun.signature} have more than two levels of indirection.`);
|
87
|
+
}
|
88
|
+
}, this);
|
89
|
+
}
|
90
|
+
|
91
|
+
private r18_7_noFlexibleArrayMembers($startNode: Joinpoint) {
|
92
|
+
for (const varDecl of Query.searchFrom($startNode, Field)) {
|
93
|
+
if (!varDecl.type.isArray || varDecl instanceof Param) continue;
|
94
|
+
if (varDecl.type.arraySize === -1) {
|
95
|
+
this.logMISRAError(varDecl, `Array ${varDecl.name} has variable or undefined size.`);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
private r18_8_noVariableLengthArrays($startNode: Joinpoint) {
|
101
|
+
for (const varDecl of Query.searchFrom($startNode, Vardecl)) {
|
102
|
+
if (!varDecl.type.isArray || varDecl.instanceOf("param")) continue;
|
103
|
+
if (varDecl.type.arraySize === -1) {
|
104
|
+
this.logMISRAError(varDecl, `Array ${varDecl.name} has variable or undefined size.`);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
2
|
+
import { Program, FileJp, Joinpoint, Class } from "@specs-feup/clava/api/Joinpoints.js";
|
3
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
4
|
+
|
5
|
+
export default class Section19_OverlappingStorage extends MISRAAnalyser {
|
6
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
7
|
+
|
8
|
+
constructor(rules: number[]) {
|
9
|
+
super(rules);
|
10
|
+
this.ruleMapper = new Map([
|
11
|
+
[2, this.r19_2_noUnion.bind(this)]
|
12
|
+
]);
|
13
|
+
}
|
14
|
+
|
15
|
+
private r19_2_noUnion($startNode: Joinpoint) {
|
16
|
+
Query.searchFrom($startNode, Class, {kind: "union"}).get().forEach(union => this.logMISRAError(union, "The union keyword should not be used."), this);
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
2
|
+
import { Program, FileJp, Joinpoint, Include } from "@specs-feup/clava/api/Joinpoints.js";
|
3
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
4
|
+
|
5
|
+
export default class Section20_PreprocessingDirectives extends MISRAAnalyser {
|
6
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
7
|
+
|
8
|
+
constructor(rules: number[]) {
|
9
|
+
super(rules);
|
10
|
+
this.ruleMapper = new Map([
|
11
|
+
[2, this.r20_2_noInvalidCharsInInclude.bind(this)]
|
12
|
+
]);
|
13
|
+
}
|
14
|
+
|
15
|
+
private r20_2_noInvalidCharsInInclude($startNode: Joinpoint) {
|
16
|
+
Query.searchFrom($startNode, Include).get().forEach(include => {
|
17
|
+
if (/.*('|"|\\|\/\*|\/\/).*/.test(include.name)) {
|
18
|
+
this.logMISRAError(include, `Invalid characters in include for ${include.name}. Invalid characters are ', ", \\, and the sequences /* and //.`)
|
19
|
+
}
|
20
|
+
}, this);
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
2
|
+
import { Call, FileJp, Include, Joinpoint, Program } from "@specs-feup/clava/api/Joinpoints.js";
|
3
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
4
|
+
|
5
|
+
export default class Section21_StandardLibraries extends MISRAAnalyser {
|
6
|
+
ruleMapper: Map<number, (jp: FileJp | Program) => void>;
|
7
|
+
|
8
|
+
constructor(rules: number[]) {
|
9
|
+
super(rules);
|
10
|
+
this.ruleMapper = new Map([
|
11
|
+
[3, this.r21_3_noDynamicAllocation.bind(this)],
|
12
|
+
[4, this.r21_4_noSetjmpUsage.bind(this)],
|
13
|
+
[5, this.r21_5_noSignalUsage.bind(this)],
|
14
|
+
[6, this.r21_6_noStandardIO.bind(this)],
|
15
|
+
[7, this.r21_7_noStringFunctions.bind(this)],
|
16
|
+
[8, this.r21_8_noSysFunctions.bind(this)],
|
17
|
+
[9, this.r21_9_noStdAlgos.bind(this)],
|
18
|
+
[10, this.r21_10_noDateUsage.bind(this)],
|
19
|
+
[11, this.r21_11_noTgmathUsage.bind(this)],
|
20
|
+
[12, this.r21_12_noFenvExceptions.bind(this)]
|
21
|
+
]);
|
22
|
+
}
|
23
|
+
|
24
|
+
private r21_3_noDynamicAllocation($startNode: Joinpoint) {
|
25
|
+
Query.searchFrom($startNode, Call, {name: /(malloc|realloc|calloc|free)/}).get().forEach(call => this.logMISRAError(call, "Use of memory allocation functions provided by <stdlib.h> is not allowed."), this);
|
26
|
+
}
|
27
|
+
|
28
|
+
private r21_4_noSetjmpUsage($startNode: Joinpoint) {
|
29
|
+
Query.searchFrom($startNode, Include, {name: "setjmp.h", isAngled: true}).get().forEach(include => this.logMISRAError(include, "Use of <setjmp.h> is not allowed."), this);
|
30
|
+
}
|
31
|
+
|
32
|
+
private r21_5_noSignalUsage($startNode: Joinpoint) {
|
33
|
+
Query.searchFrom($startNode, Include, {name: "signal.h", isAngled: true}).get().forEach(include => this.logMISRAError(include, "Use of <setjmp.h> is not allowed."), this);
|
34
|
+
}
|
35
|
+
|
36
|
+
private r21_6_noStandardIO($startNode: Joinpoint) {
|
37
|
+
Query.searchFrom($startNode, Include, {name: /(stdio.h|wchar.h)/}).get().forEach(include => this.logMISRAError(include, "Use of the standard library I/O functions provided by <stdio.h> and <wchar.h> is not allowed."), this);
|
38
|
+
}
|
39
|
+
|
40
|
+
//how to avoid triggering errors for functions with the same name?
|
41
|
+
private r21_7_noStringFunctions($startNode: Joinpoint) {
|
42
|
+
Query.searchFrom($startNode, Call, {name: /(atoi|atof|atol|atoll)/}).get().forEach(call => this.logMISRAError(call, `Use of function ${call.signature} is not allowed.`), this);
|
43
|
+
}
|
44
|
+
|
45
|
+
private r21_8_noSysFunctions($startNode: Joinpoint) {
|
46
|
+
Query.searchFrom($startNode, Call, {name: /(system|abort|exit|getenv)/}).get().forEach(call => this.logMISRAError(call, `Use of function ${call.signature} is not allowed.`), this);
|
47
|
+
}
|
48
|
+
|
49
|
+
private r21_9_noStdAlgos($startNode: Joinpoint) {
|
50
|
+
Query.searchFrom($startNode, Call, {name: /(qsort|bsearch)/}).get().forEach(call => this.logMISRAError(call, `Use of function ${call.signature} is not allowed.`), this);
|
51
|
+
}
|
52
|
+
|
53
|
+
private r21_10_noDateUsage($startNode: Joinpoint) {
|
54
|
+
Query.searchFrom($startNode, Include, {name: "time.h", isAngled: true}).get().forEach(include => this.logMISRAError(include, "Use of <time.h> is not allowed."), this);
|
55
|
+
Query.searchFrom($startNode, Call, {name: "wcsftime"}).get().forEach(call => this.logMISRAError(call, "Identifier 'wcsftime' shall not be used."), this);
|
56
|
+
}
|
57
|
+
|
58
|
+
private r21_11_noTgmathUsage($startNode: Joinpoint) {
|
59
|
+
Query.searchFrom($startNode, Include, {name: "tgmath.h", isAngled: true}).get().forEach(include => this.logMISRAError(include, "Use of <tgmath.h> is not allowed."), this);
|
60
|
+
}
|
61
|
+
|
62
|
+
private r21_12_noFenvExceptions($startNode: Joinpoint) {
|
63
|
+
Query.searchFrom($startNode, Call, {name : /(feclearexcept|fegetexceptflag|feraiseexcept|fesetexceptflag|fetestexcept)/}).get().forEach(call => this.logMISRAError(call, `Use of function ${call.signature} is not allowed`), this);
|
64
|
+
}
|
65
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { Program, FileJp, Joinpoint, FunctionJp, Varref, Param, LabelStmt, GotoStmt } from "@specs-feup/clava/api/Joinpoints.js";
|
2
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
3
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
4
|
+
import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
|
5
|
+
|
6
|
+
export default class Section2_UnusedCode extends MISRAAnalyser {
|
7
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
8
|
+
|
9
|
+
constructor(rules: number[]) {
|
10
|
+
super(rules);
|
11
|
+
this.ruleMapper = new Map([
|
12
|
+
[6, this.r2_6_noUnusedLabels.bind(this)],
|
13
|
+
[7, this.r2_7_noUnusedParams.bind(this)]
|
14
|
+
]);
|
15
|
+
}
|
16
|
+
|
17
|
+
private r2_6_noUnusedLabels($startNode: Joinpoint) {
|
18
|
+
Query.searchFrom($startNode, FunctionJp).get().forEach(fun => {
|
19
|
+
Query.searchFrom(fun, LabelStmt).get().forEach(label => {
|
20
|
+
if (Query.searchFrom(fun, GotoStmt, {label: jp => jp.astId == label.decl.astId}).get().length === 0) {
|
21
|
+
this.logMISRAError(label, `Label ${label.decl.name} is unused in function ${fun.name}.`);
|
22
|
+
}
|
23
|
+
},this);
|
24
|
+
}, this);
|
25
|
+
}
|
26
|
+
|
27
|
+
private r2_7_noUnusedParams($startNode: Joinpoint) {
|
28
|
+
Query.searchFrom($startNode, FunctionJp).get().forEach(fun => {
|
29
|
+
const params: Map<string, Param> = new Map();
|
30
|
+
for (const param of fun.params) {
|
31
|
+
params.set(param.astId, param);
|
32
|
+
}
|
33
|
+
|
34
|
+
Query.searchFrom(fun, Varref).get().forEach(ref => {
|
35
|
+
params.delete(ref.decl?.astId);
|
36
|
+
}, this);
|
37
|
+
|
38
|
+
params.forEach((v, k, m) => {
|
39
|
+
this.logMISRAError(v, `Parameter ${v.name} is unused.`, new Fix(v, $jp => {
|
40
|
+
const fun = $jp.getAncestor("function") as FunctionJp;
|
41
|
+
const params = fun.params.filter(param => param.astId !== $jp.astId);
|
42
|
+
fun.setParams(params);
|
43
|
+
}));
|
44
|
+
}, this);
|
45
|
+
}, this);
|
46
|
+
}
|
47
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { Program, FileJp, Joinpoint, Comment } from "@specs-feup/clava/api/Joinpoints.js";
|
2
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
3
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
4
|
+
|
5
|
+
export default class Section3_Comments extends MISRAAnalyser {
|
6
|
+
protected ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
7
|
+
|
8
|
+
constructor(rules: number[]) {
|
9
|
+
super(rules);
|
10
|
+
this.ruleMapper = new Map([
|
11
|
+
[3, this.r3_1_fixComments.bind(this)]
|
12
|
+
]);
|
13
|
+
}
|
14
|
+
|
15
|
+
private static removeCommentSequences(str: string) {
|
16
|
+
return str.replace(/(\/\/||\/\*)/g, '');
|
17
|
+
}
|
18
|
+
|
19
|
+
private r3_1_fixComments($startNode: Joinpoint) { //inlines
|
20
|
+
Query.search(Comment).get().forEach(comment => comment.setText(Section3_Comments.removeCommentSequences(comment.text)));
|
21
|
+
//Query.searchFrom(Query.root()).get().forEach(jp => jp.setInlineComments(jp.inlineComments.map(comment => removeCommentSequences(comment.text).toString())));
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,149 @@
|
|
1
|
+
import { Program, FileJp, Joinpoint, StorageClass, Vardecl, FunctionJp, TypedefDecl, NamedDecl, TypedefNameDecl, Class, TagType, EnumDecl } from "@specs-feup/clava/api/Joinpoints.js";
|
2
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
3
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
4
|
+
import Fix from "@specs-feup/clava/api/clava/analysis/Fix.js";
|
5
|
+
|
6
|
+
export default class Section5_Identifiers extends MISRAAnalyser {
|
7
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
8
|
+
|
9
|
+
constructor(rules: number[]) {
|
10
|
+
super(rules);
|
11
|
+
this.ruleMapper = new Map([
|
12
|
+
[1, this.r5_1_externalIdentifierLength.bind(this)],
|
13
|
+
[6, this.r5_6_uniqueTypedefs.bind(this)],
|
14
|
+
[7, this.r5_7_uniqueTags.bind(this)],
|
15
|
+
[8, this.r5_8_uniqueExternalIds.bind(this)]
|
16
|
+
]);
|
17
|
+
}
|
18
|
+
|
19
|
+
private static hasExternalLinkage($class: StorageClass) {
|
20
|
+
return $class !== StorageClass.STATIC && $class !== StorageClass.EXTERN;
|
21
|
+
}
|
22
|
+
|
23
|
+
private r5_1_externalIdentifierLength($startNode: Joinpoint) {
|
24
|
+
const prefixes: Set<string> = new Set();
|
25
|
+
for (const vardecl of Query.searchFrom($startNode, Vardecl, {storageClass: (sC: StorageClass) => Section5_Identifiers.hasExternalLinkage(sC)})) {
|
26
|
+
if (vardecl.name.length >= 31) {
|
27
|
+
if (prefixes.has(vardecl.name.substring(0, 32))) {
|
28
|
+
this.logMISRAError(vardecl, `External identifier ${vardecl.name} is not distinct.`, new Fix(vardecl, ($jp) => {
|
29
|
+
($jp as Vardecl).name = "a_" + ($jp as Vardecl).name;
|
30
|
+
}));
|
31
|
+
}
|
32
|
+
else {
|
33
|
+
prefixes.add(vardecl.name.substring(0, 32));
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
for (const fun of Query.searchFrom($startNode, FunctionJp, {storageClass: (sC: StorageClass) => Section5_Identifiers.hasExternalLinkage(sC)})) {
|
39
|
+
if (fun.name.length >= 31) {
|
40
|
+
if (prefixes.has(fun.name.substring(0, 32))) {
|
41
|
+
this.logMISRAError(fun, `External identifier ${fun.name} is not distinct.`, new Fix(fun, ($jp) => {
|
42
|
+
($jp as FunctionJp).name = "a_" + ($jp as FunctionJp).name;
|
43
|
+
}));
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
prefixes.add(fun.name.substring(0, 32));
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
private r5_6_uniqueTypedefs($startNode: Joinpoint) { //exception is missing
|
53
|
+
const typedefs: Set<string> = new Set();
|
54
|
+
|
55
|
+
for (const typedef of Query.searchFrom($startNode, TypedefDecl)) {
|
56
|
+
if (typedefs.has(typedef.name)) {
|
57
|
+
this.logMISRAError(typedef, "Typedef names must be unique across all translation units.", new Fix(typedef, jp => {
|
58
|
+
const typedefJp = jp as TypedefDecl;
|
59
|
+
typedefJp.name = typedef.name + "_" + typedefJp.astId;
|
60
|
+
}));
|
61
|
+
}
|
62
|
+
else {
|
63
|
+
typedefs.add(typedef.name);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
Query.searchFrom($startNode, NamedDecl).get().filter(decl => !(decl instanceof TypedefNameDecl)).forEach(decl => {
|
68
|
+
if (decl instanceof Class) {
|
69
|
+
const typedefChildren = decl.getDescendants("typedefDecl") as TypedefDecl[];
|
70
|
+
for (const child of typedefChildren) {
|
71
|
+
if (decl.name === child.name) return;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
if (typedefs.has(decl.name)) {
|
75
|
+
this.logMISRAError(decl, `${decl.name} is also the name of a typedef. Typedef identifiers must not be reused.`, new Fix(decl, jp => {
|
76
|
+
const declJp = jp as NamedDecl;
|
77
|
+
declJp.name = declJp.name + "_" + declJp.astId;
|
78
|
+
}));
|
79
|
+
}
|
80
|
+
}, this);
|
81
|
+
}
|
82
|
+
|
83
|
+
private r5_7_uniqueTags($startNode: Joinpoint) {
|
84
|
+
const tags: Set<string> = new Set();
|
85
|
+
|
86
|
+
for (const classJp of Query.searchFrom($startNode, Class)) {
|
87
|
+
if (classJp.type instanceof TagType) {
|
88
|
+
if (tags.has(classJp.type.name)) {
|
89
|
+
this.logMISRAError(classJp, "Tag names must be unique across all translation units.");
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
tags.add(classJp.type.name);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
for (const enumDecl of Query.searchFrom($startNode, EnumDecl)) {
|
98
|
+
if (enumDecl.type instanceof TagType) {
|
99
|
+
if (tags.has(enumDecl.type.name)) {
|
100
|
+
this.logMISRAError(enumDecl, "Tag names must be unique across all translation units.");
|
101
|
+
}
|
102
|
+
else {
|
103
|
+
tags.add(enumDecl.type.name);
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
Query.searchFrom($startNode, NamedDecl).get().filter(decl => !(decl instanceof TypedefNameDecl)).forEach(decl => {
|
109
|
+
if (decl instanceof Class) {
|
110
|
+
const typedefChildren = decl.getDescendants("typedefDecl") as TypedefDecl[];
|
111
|
+
for (const child of typedefChildren) {
|
112
|
+
if (decl.name === child.name) return;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
if (decl.type instanceof TagType) return;
|
116
|
+
if (tags.has(decl.name)) {
|
117
|
+
this.logMISRAError(decl, `${decl.name} is also the name of a tag. Tag identifiers must not be reused.`, new Fix(decl, jp => {
|
118
|
+
const declJp = jp as NamedDecl;
|
119
|
+
declJp.name = declJp.name + "_" + declJp.astId;
|
120
|
+
}));
|
121
|
+
}
|
122
|
+
}, this);
|
123
|
+
}
|
124
|
+
|
125
|
+
private r5_8_uniqueExternalIds($startNode: Joinpoint) {
|
126
|
+
const ids: Set<string> = new Set();
|
127
|
+
for (const vardecl of Query.searchFrom($startNode, Vardecl, {storageClass: (sC: StorageClass) => Section5_Identifiers.hasExternalLinkage(sC)})) {
|
128
|
+
if (ids.has(vardecl.name)) {
|
129
|
+
this.logMISRAError(vardecl, `External identifier ${vardecl.name} must be unique across all translation units.`, new Fix(vardecl, ($jp) => {
|
130
|
+
($jp as Vardecl).name = "a_" + ($jp as Vardecl).name;
|
131
|
+
}));
|
132
|
+
}
|
133
|
+
else {
|
134
|
+
ids.add(vardecl.name);
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
for (const fun of Query.searchFrom($startNode, FunctionJp, {storageClass: (sC: StorageClass) => Section5_Identifiers.hasExternalLinkage(sC)})) {
|
139
|
+
if (ids.has(fun.name)) {
|
140
|
+
this.logMISRAError(fun, `External identifier ${fun.name} must be unique across all translation units.`, new Fix(fun, ($jp) => {
|
141
|
+
($jp as FunctionJp).name = "a_" + ($jp as FunctionJp).name;
|
142
|
+
}));
|
143
|
+
}
|
144
|
+
else {
|
145
|
+
ids.add(fun.name);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
2
|
+
import { Program, FileJp, Joinpoint, Field, IntLiteral, BuiltinType } from "@specs-feup/clava/api/Joinpoints.js";
|
3
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
4
|
+
|
5
|
+
export default class Section6_Types extends MISRAAnalyser {
|
6
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
7
|
+
|
8
|
+
constructor(rules: number[]) {
|
9
|
+
super(rules);
|
10
|
+
this.ruleMapper = new Map([
|
11
|
+
[2, this.r6_2_noSingleBitSignedFields.bind(this)],
|
12
|
+
|
13
|
+
]);
|
14
|
+
}
|
15
|
+
|
16
|
+
private r6_2_noSingleBitSignedFields($startNode: Joinpoint) {
|
17
|
+
Query.searchFrom($startNode, Field).get().forEach(field => {
|
18
|
+
if (field.children.length > 0 && field.name) {
|
19
|
+
const width = new Number((field.children[0].children[0] as IntLiteral).value);
|
20
|
+
if (width == 1 && (field.type as BuiltinType).isSigned) {
|
21
|
+
this.logMISRAError(field, `Single-bit named bit field ${field.name} must not have a signed type.`);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}, this);
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import Query from "@specs-feup/lara/api/weaver/Query.js";
|
2
|
+
import { Program, FileJp, IntLiteral, Type, PointerType, QualType, Vardecl, BinaryOp, ReturnStmt, FunctionJp, Call, Varref, ArrayAccess, Joinpoint } from "@specs-feup/clava/api/Joinpoints.js";
|
3
|
+
import MISRAAnalyser from "../MISRAAnalyser.js";
|
4
|
+
|
5
|
+
export default class Section7_LiteralsConstants extends MISRAAnalyser {
|
6
|
+
ruleMapper: Map<number, (jp: Program | FileJp) => void>;
|
7
|
+
|
8
|
+
constructor(rules: number[]) {
|
9
|
+
super(rules);
|
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
|
+
private r7_1_noOctalConstants($startNode: Joinpoint) {
|
18
|
+
for (const intLit of Query.searchFrom($startNode, IntLiteral)) {
|
19
|
+
if (intLit.code.match(/0[0-9]+/g)) {
|
20
|
+
this.logMISRAError(intLit, `The octal constant ${intLit.code} was used. Its decimal value is ${intLit.value}`);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
private r7_3_noLowercaseLSuffix($startNode: Joinpoint) {
|
26
|
+
for (const intLit of Query.searchFrom($startNode, IntLiteral)) {
|
27
|
+
if (intLit.code.includes('l')) {
|
28
|
+
this.logMISRAError(intLit, `A lowercase 'l' was used as a suffix in ${intLit.code}.`);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
private static checkPointerConst(type: Type) {
|
34
|
+
if (type instanceof PointerType) {
|
35
|
+
return type.pointee.constant;
|
36
|
+
}
|
37
|
+
else if (type instanceof QualType && type.unqualifiedType instanceof PointerType) {
|
38
|
+
return type.unqualifiedType.pointee.constant;
|
39
|
+
}
|
40
|
+
else return undefined;
|
41
|
+
}
|
42
|
+
|
43
|
+
private r7_4_constStringLiterals($startNode: Joinpoint) {
|
44
|
+
for (const varDecl of Query.searchFrom($startNode, Vardecl)) {
|
45
|
+
if (!varDecl.type.isPointer) continue;
|
46
|
+
|
47
|
+
if (varDecl.children.length > 0 && varDecl.children[0].joinPointType === "literal"
|
48
|
+
&& !Section7_LiteralsConstants.checkPointerConst(varDecl.type)) {
|
49
|
+
this.logMISRAError(varDecl, `String literal assigned to non-const qualified variable ${varDecl.name}`);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
Query.searchFrom($startNode, BinaryOp, {isAssignment: true}).get().forEach(bOp => {
|
54
|
+
if (bOp.right.joinPointType === "literal" && !Section7_LiteralsConstants.checkPointerConst(bOp.left.type)) {
|
55
|
+
this.logMISRAError(bOp, `String literal assigned to non-const qualified variable ${(bOp.left as Varref | ArrayAccess).name}`);
|
56
|
+
}
|
57
|
+
}, this);
|
58
|
+
|
59
|
+
for (const ret of Query.searchFrom($startNode, ReturnStmt)) {
|
60
|
+
const ancestor = ret.getAncestor("function") as FunctionJp;
|
61
|
+
const retType = ancestor.functionType.returnType;
|
62
|
+
if (ret.returnExpr?.joinPointType === "literal" && !Section7_LiteralsConstants.checkPointerConst(retType)) {
|
63
|
+
this.logMISRAError(ret, `String literal returned in non-const qualified return value for function ${ancestor.name}`);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
for (const call of Query.searchFrom($startNode, Call)) {
|
68
|
+
const paramTypes = call.function.functionType.paramTypes;
|
69
|
+
for (let i = 1; i < call.children.length; i++) {
|
70
|
+
if (call.children[i].joinPointType === "literal" && !Section7_LiteralsConstants.checkPointerConst(paramTypes[i-1])) {
|
71
|
+
this.logMISRAError(call.children[i], `String literal passed as non-const qualified parameter in call of ${call.function.name}`);
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|