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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | 2129x 2129x 1611x 1611x 2x 1609x 2x 1607x 2x 1605x 504x 236x 504x 503x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 927x 27x 1448x 1448x 1556x 1556x 1079x | /**
* Which C-Next function a spelling denotes.
*
* #1322. Three questions in pass 2.1 end at the program's symbol for one
* function: "what does this call target?" (E0878's callee, E0708's), "which
* function is this VALUE?" (a function placed into a callback-typed slot,
* ADR-029) and "which function does this TYPE name?" (the slot itself, whose
* declared type is a function under ADR-029's function-as-type rule). All
* three start from the same spellings -- `f`, `this.f`, `global.f`,
* `Scope.f` -- and `CalleeNameResolver` owns turning a spelling into a
* candidate name. What this class owns is the LOOKUP: which candidates are
* tried, in which order, against the program.
*
* ## The order is the decision
*
* ADR-057: inside a scope, a bare `f` means the scope's own `f` when one
* exists, and the global `f` otherwise -- codegen emits `S__f()` for a bare
* `f()` inside `S` whichever of the two is declared. Before this class, two
* analyzers held that order independently and one held it backwards: the
* ADR-070 check asked "is the GLOBAL `f` non-void?" first and fell back to the
* scope's, so with a global `u8 f()` beside a scope's `void f()` it rejected
* a bare `f();` that codegen lowered to the void call. `candidates` is now the
* one place the order is written, and every lookup walks it.
*/
import * as Parser from "../../../PARSE/2-Parse/grammar/CNextParser";
import IFunctionSymbol from "../../../types/symbols/IFunctionSymbol";
import ExpressionUnwrapper from "../../../utils/ExpressionUnwrapper";
import QualifiedCName from "../../../utils/QualifiedCName";
import ScopeUtils from "../../../utils/ScopeUtils";
import TypeText from "./TypeText";
import CalleeNameResolver from "./CalleeNameResolver";
import ScopeCandidates from "./ScopeCandidates";
import type IAnalysisContext from "../types/IAnalysisContext";
class FunctionReference {
/**
* The C names a resolved spelling may denote, most specific first: the
* enclosing scope's member (ADR-057), then the name as resolved. A
* `global.`-qualified or already-qualified name has one candidate.
*/
static candidates(
resolvedName: string,
scopePath: string,
isGlobalCall: boolean,
): string[] {
// `scopeQualifiedCandidate` already returns null for a `global.` call, so
// the root passed here is null: the search order is what differs, and it is
// decided in one place.
const scoped = CalleeNameResolver.scopeQualifiedCandidate(
resolvedName,
scopePath,
isGlobalCall,
);
return ScopeCandidates.forRoot(null, scoped, [resolvedName]);
}
/**
* The candidates for a TYPE spelling as written in a declaration:
* `onDown`, `this.handler`, `global.onDown`, `S.handler`, or the C name a
* struct field's type is recorded under. An array spelling names its
* element type.
*/
static candidatesForTypeText(typeText: string, scopePath: string): string[] {
const text = TypeText.withoutDimensions(typeText);
if (text.startsWith("this.")) {
return scopePath === ""
? []
: [ScopeUtils.qualifyInScope(text.slice("this.".length), scopePath)];
}
if (text.startsWith("global.")) {
return [text.slice("global.".length)];
}
if (text.includes(".")) {
return [QualifiedCName.fromParts(text.split("."))];
}
return FunctionReference.candidates(text, scopePath, false);
}
/**
* The function a call chain targets, or null: a foreign function, a call
* through a value (`p.handler(5)`), or a name the program does not declare.
*/
static ofCall(
postfix: Parser.PostfixExpressionContext,
scopePath: string,
context: IAnalysisContext,
): IFunctionSymbol | null {
const resolved = CalleeNameResolver.resolveDetailed(
postfix,
scopePath,
(name) => context.symbols.knownScopes.has(name),
);
if (resolved === null) return null;
return FunctionReference.lookup(
FunctionReference.candidates(
resolved.name,
scopePath,
resolved.isGlobalCall,
),
context,
);
}
/**
* The function an expression NAMES without calling it -- `onUp`,
* `this.handler`, `global.onUp`, `S.handler` -- or null for anything else:
* a variable of callback type, a call, a subscript, an expression.
*/
static ofValue(
expression: Parser.ExpressionContext,
scopePath: string,
context: IAnalysisContext,
): IFunctionSymbol | null {
const postfix = ExpressionUnwrapper.getPostfixExpression(expression);
Iif (postfix === null) return null;
const ops = postfix.postfixOp();
Iif (ops.some((op) => op.DOT() === null)) return null;
const base = CalleeNameResolver.baseName(postfix.primaryExpression());
Iif (base === null) return null;
let name = base;
for (const op of ops) {
const next = CalleeNameResolver.resolveMemberAccess(
name,
op,
scopePath,
(name) => context.symbols.knownScopes.has(name),
);
if (next === null) return null;
name = next;
}
Iif (name === "this" || name === "global") return null;
return FunctionReference.lookup(
FunctionReference.candidates(name, scopePath, base === "global"),
context,
);
}
/** The function a declared TYPE names, or null when the type is not one. */
static ofTypeText(
typeText: string,
scopePath: string,
context: IAnalysisContext,
): IFunctionSymbol | null {
return FunctionReference.lookup(
FunctionReference.candidatesForTypeText(typeText, scopePath),
context,
);
}
/** The symbol's C name -- the identity ADR-029's nominal rule compares. */
static cNameOf(symbol: IFunctionSymbol): string {
return ScopeUtils.getTranspiledCName(symbol);
}
private static lookup(
candidates: readonly string[],
context: IAnalysisContext,
): IFunctionSymbol | null {
const program = context.program;
for (const cName of candidates) {
const symbol = program.symbolByCName(cName);
if (symbol?.kind === "function") return symbol;
}
return null;
}
}
export default FunctionReference;
|