Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 275x 614x 1711x 2325x 91x 2234x 258x 1976x 1976x | import type SymbolTable from "./SymbolTable";
import type TSymbol from "../../types/symbols/TSymbol";
/**
* Issues #1161 and #1164 — the single answer to "which symbols form this
* file's public C interface?"
*
* Two predicates used to answer that question and disagreed. One decided
* whether a `.h` was written (`isExported`, counting functions, structs, enums,
* bitmaps and consts); the other decided whether the generated `.c` included it
* (scope-member visibility, which nothing declared at top level can satisfy).
* When they disagreed a header was written that nothing included, so every
* external-linkage definition in that `.c` lost its visible declaration
* (MISRA C:2012 Rule 8.4) and the `.c` redefined inline the very types the
* header already declared.
*
* Both decisions now resolve here. Callers must ask this class rather than
* re-derive the answer — two callers that merely agree today are a latent
* divergence, which is what the two issues above were.
*/
class PublicInterface {
/**
* The symbols that make up this file's generated header, in collection order.
*/
static forFile(symbolTable: SymbolTable, sourcePath: string): TSymbol[] {
return symbolTable
.getTSymbolsByFile(sourcePath)
.filter((symbol) => PublicInterface.isHeaderVisible(symbol));
}
/**
* Whether these symbols form a public C interface: a header will be
* generated, and the generated `.c` must include it.
*
* Takes the file's symbols rather than reading global state, so the `.c` and
* its header cannot be decided from two different snapshots.
*/
static existsIn(symbols: readonly TSymbol[]): boolean {
return symbols.some((symbol) => PublicInterface.isHeaderVisible(symbol));
}
/**
* Whether this symbol contributes a declaration to the generated header.
*/
private static isHeaderVisible(symbol: TSymbol): boolean {
if (!symbol.isExported) {
return false;
}
// A scope is a container, not a declaration. Its members are collected as
// symbols in their own right, and no header path emits anything for kind
// "scope". Counting it would produce a header holding only include guards
// for a scope whose members are all private — and, once the `.c` includes
// whatever header exists, a self-include of that empty file.
if (symbol.kind === "scope") {
return false;
}
return !PublicInterface.isTopLevelMain(symbol);
}
/**
* ADR-030: `main` has external linkage but is called by the C runtime, never
* by another translation unit, so a prototype serves no consumer. MISRA
* C:2012 Rule 8.4 exempts it for that reason while requiring a visible
* declaration for every other external-linkage definition.
*
* Scoped members are not exempt: a `main` inside `scope Sample` transpiles to
* `Sample__main`, which is an ordinary cross-file callee.
*/
private static isTopLevelMain(symbol: TSymbol): boolean {
return (
symbol.kind === "function" &&
symbol.name === "main" &&
symbol.scope.name === ""
);
}
}
export default PublicInterface;
|