fix(lint): constexpr-constant asks clang's evaluator, not the initialiser tokens
The first cut scanned the initialiser's tokens and required every one to be a literal or an operator. That is exactly the kind of approximation this work has been removing, and it was wrong in both directions: const int A = sizeof(Big); missed — `sizeof` is a keyword const int B = Base + 1; missed — `Base` is an identifier const auto C = 5_notConstexpr; would have been reported, wrongly clang_Cursor_Evaluate answers the question directly. Evaluating a variable declaration evaluates its initialiser, so anything that folds is recognised and a call result still is not. Costs nothing measurable — lint stays at ~15s. Found one real case the token version could not see: an EShMessages fold over three glslang enum constants in Crafter.Build-Shader.cpp. Promoting it to constexpr then made `naming` ask for constant naming, since a constexpr variable is a compile-time constant — so it is `Messages` now. Two rules agreeing on the same declaration is the intended behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
651720e494
commit
649d64ae12
5 changed files with 52 additions and 31 deletions
|
|
@ -96,6 +96,9 @@ namespace {
|
|||
decltype(&clang_getArgType) getArgType = nullptr;
|
||||
decltype(&clang_getNumArgTypes) getNumArgTypes = nullptr;
|
||||
decltype(&clang_getPointeeType) getPointeeType = nullptr;
|
||||
decltype(&clang_Cursor_Evaluate) evaluate = nullptr;
|
||||
decltype(&clang_EvalResult_getKind) evalResultKind = nullptr;
|
||||
decltype(&clang_EvalResult_dispose) disposeEvalResult = nullptr;
|
||||
};
|
||||
|
||||
LibClang LoadLibClang() {
|
||||
|
|
@ -182,6 +185,9 @@ namespace {
|
|||
bind(lib.getArgType, "clang_getArgType");
|
||||
bind(lib.getNumArgTypes, "clang_getNumArgTypes");
|
||||
bind(lib.getPointeeType, "clang_getPointeeType");
|
||||
bind(lib.evaluate, "clang_Cursor_Evaluate");
|
||||
bind(lib.evalResultKind, "clang_EvalResult_getKind");
|
||||
bind(lib.disposeEvalResult, "clang_EvalResult_dispose");
|
||||
bind(lib.getBinaryOpcode, "clang_Cursor_getBinaryOpcode");
|
||||
bind(lib.getUnaryOperatorKind, "clang_getCursorUnaryOperatorKind");
|
||||
bind(lib.isConstQualifiedType, "clang_isConstQualifiedType");
|
||||
|
|
@ -190,6 +196,9 @@ namespace {
|
|||
bind(lib.getArgType, "clang_getArgType");
|
||||
bind(lib.getNumArgTypes, "clang_getNumArgTypes");
|
||||
bind(lib.getPointeeType, "clang_getPointeeType");
|
||||
bind(lib.evaluate, "clang_Cursor_Evaluate");
|
||||
bind(lib.evalResultKind, "clang_EvalResult_getKind");
|
||||
bind(lib.disposeEvalResult, "clang_EvalResult_dispose");
|
||||
if (!missing.empty()) {
|
||||
lib.handle = nullptr;
|
||||
lib.error = std::format("loaded {} but it is missing {}", candidates.front(), join(missing));
|
||||
|
|
@ -655,6 +664,18 @@ namespace {
|
|||
decl.isConst = lc.isConstQualifiedType(declaredType) != 0;
|
||||
decl.isScalar = IsScalarTypeKind(declaredType.kind);
|
||||
decl.isLoopVariable = walk.inLoopBinding;
|
||||
// Ask clang whether the initialiser is a constant expression instead of
|
||||
// inspecting its tokens. Evaluating a VarDecl evaluates its initialiser,
|
||||
// so this covers sizeof, a fold over other constants, and anything else
|
||||
// that folds — none of which a token scan can recognise — and it does
|
||||
// not mistake a literal with a non-constexpr user-defined suffix for a
|
||||
// constant.
|
||||
if (mapped == LintDeclKind::Variable || mapped == LintDeclKind::Field) {
|
||||
if (CXEvalResult evaluated = lc.evaluate(cursor)) {
|
||||
decl.isConstantInitialised = lc.evalResultKind(evaluated) != CXEval_UnExposed;
|
||||
lc.disposeEvalResult(evaluated);
|
||||
}
|
||||
}
|
||||
if (mapped == LintDeclKind::Method) {
|
||||
decl.isConstMethod = lc.methodIsConst(cursor) != 0;
|
||||
decl.isStaticMethod = lc.methodIsStatic(cursor) != 0;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace Crafter {
|
|||
return out;
|
||||
};
|
||||
|
||||
const EShMessages messages = static_cast<EShMessages>(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules);
|
||||
constexpr EShMessages Messages = static_cast<EShMessages>(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules);
|
||||
std::ifstream fileStream(path, std::ios::in | std::ios::binary);
|
||||
if (!fileStream) {
|
||||
return fail("failed to open shader source", {});
|
||||
|
|
@ -86,13 +86,13 @@ namespace Crafter {
|
|||
includeDir.pushExternalLocalDirectory(dir.generic_string());
|
||||
}
|
||||
|
||||
if (!shader.parse(GetDefaultResources(), 100, false, messages, includeDir)) {
|
||||
if (!shader.parse(GetDefaultResources(), 100, false, Messages, includeDir)) {
|
||||
return fail("GLSL parse failed", std::string(shader.getInfoLog()) + shader.getInfoDebugLog());
|
||||
}
|
||||
|
||||
glslang::TProgram program;
|
||||
program.addShader(&shader);
|
||||
if (!program.link(messages)) {
|
||||
if (!program.link(Messages)) {
|
||||
return fail("GLSL link failed", std::string(program.getInfoLog()) + program.getInfoDebugLog());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue