c-next 0.1.24 → 0.1.25
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/package.json
CHANGED
|
@@ -1430,13 +1430,17 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1430
1430
|
// Issue #230: Self-include for extern "C" linkage
|
|
1431
1431
|
// When file has public symbols and headers are being generated,
|
|
1432
1432
|
// include own header to ensure proper C linkage
|
|
1433
|
+
// Issue #339: Use relative path from source root when available
|
|
1433
1434
|
if (
|
|
1434
1435
|
options?.generateHeaders &&
|
|
1435
1436
|
this.symbols!.hasPublicSymbols() &&
|
|
1436
1437
|
this.sourcePath
|
|
1437
1438
|
) {
|
|
1438
|
-
|
|
1439
|
-
|
|
1439
|
+
// Issue #339: Prefer sourceRelativePath for correct directory structure
|
|
1440
|
+
// Otherwise fall back to basename for backward compatibility
|
|
1441
|
+
const pathToUse =
|
|
1442
|
+
options.sourceRelativePath || this.sourcePath.replace(/^.*[\\/]/, "");
|
|
1443
|
+
const headerName = pathToUse.replace(/\.cnx$|\.cnext$/, ".h");
|
|
1440
1444
|
output.push(`#include "${headerName}"`);
|
|
1441
1445
|
output.push("");
|
|
1442
1446
|
}
|
|
@@ -18,6 +18,12 @@ interface ICodeGeneratorOptions {
|
|
|
18
18
|
* Uses temporary variables instead of compound literals for rvalue pointer params.
|
|
19
19
|
*/
|
|
20
20
|
cppMode?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Issue #339: Relative path from source root to source file for self-include.
|
|
23
|
+
* When set, self-includes will use this relative path instead of just the basename.
|
|
24
|
+
* Example: "Display/Utils.cnx" -> #include "Display/Utils.h"
|
|
25
|
+
*/
|
|
26
|
+
sourceRelativePath?: string;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export default ICodeGeneratorOptions;
|
package/src/pipeline/Pipeline.ts
CHANGED
|
@@ -642,6 +642,7 @@ class Pipeline {
|
|
|
642
642
|
sourcePath: file.path, // Issue #230: For self-include header generation
|
|
643
643
|
generateHeaders: this.config.generateHeaders, // Issue #230: Enable self-include when headers are generated
|
|
644
644
|
cppMode: this.cppDetected, // Issue #250: C++ compatible code generation
|
|
645
|
+
sourceRelativePath: this.getSourceRelativePath(file.path), // Issue #339: For correct self-include paths
|
|
645
646
|
},
|
|
646
647
|
);
|
|
647
648
|
|
|
@@ -699,14 +700,14 @@ class Pipeline {
|
|
|
699
700
|
}
|
|
700
701
|
|
|
701
702
|
/**
|
|
702
|
-
* Get
|
|
703
|
+
* Get relative path from any input directory for a file.
|
|
704
|
+
* Returns the relative path (e.g., "Display/Utils.cnx") or null if the file
|
|
705
|
+
* is not under any input directory.
|
|
706
|
+
*
|
|
707
|
+
* This is the shared logic used by getSourceRelativePath, getOutputPath,
|
|
708
|
+
* and getHeaderOutputPath for directory structure preservation.
|
|
703
709
|
*/
|
|
704
|
-
private
|
|
705
|
-
// Issue #211: Derive extension from cppDetected flag
|
|
706
|
-
const ext = this.cppDetected ? ".cpp" : ".c";
|
|
707
|
-
const outputName = basename(file.path).replace(/\.cnx$|\.cnext$/, ext);
|
|
708
|
-
|
|
709
|
-
// Check if file is in any input directory (for preserving structure)
|
|
710
|
+
private getRelativePathFromInputs(filePath: string): string | null {
|
|
710
711
|
for (const input of this.config.inputs) {
|
|
711
712
|
const resolvedInput = resolve(input);
|
|
712
713
|
|
|
@@ -715,24 +716,49 @@ class Pipeline {
|
|
|
715
716
|
continue;
|
|
716
717
|
}
|
|
717
718
|
|
|
718
|
-
const relativePath = relative(resolvedInput,
|
|
719
|
+
const relativePath = relative(resolvedInput, filePath);
|
|
719
720
|
|
|
720
721
|
// Check if file is under this input directory
|
|
721
722
|
if (relativePath && !relativePath.startsWith("..")) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
723
|
+
return relativePath;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
725
726
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Issue #339: Get relative path from input directory for self-include generation.
|
|
732
|
+
* Returns the relative path (e.g., "Display/Utils.cnx") or just the basename
|
|
733
|
+
* if the file is not in any input directory.
|
|
734
|
+
*/
|
|
735
|
+
private getSourceRelativePath(filePath: string): string {
|
|
736
|
+
return this.getRelativePathFromInputs(filePath) ?? basename(filePath);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Get output path for a file
|
|
741
|
+
*/
|
|
742
|
+
private getOutputPath(file: IDiscoveredFile): string {
|
|
743
|
+
// Issue #211: Derive extension from cppDetected flag
|
|
744
|
+
const ext = this.cppDetected ? ".cpp" : ".c";
|
|
745
|
+
|
|
746
|
+
const relativePath = this.getRelativePathFromInputs(file.path);
|
|
747
|
+
if (relativePath) {
|
|
748
|
+
// File is under an input directory - preserve structure
|
|
749
|
+
const outputRelative = relativePath.replace(/\.cnx$|\.cnext$/, ext);
|
|
750
|
+
const outputPath = join(this.config.outDir, outputRelative);
|
|
730
751
|
|
|
731
|
-
|
|
752
|
+
const outputDir = dirname(outputPath);
|
|
753
|
+
if (!existsSync(outputDir)) {
|
|
754
|
+
mkdirSync(outputDir, { recursive: true });
|
|
732
755
|
}
|
|
756
|
+
|
|
757
|
+
return outputPath;
|
|
733
758
|
}
|
|
734
759
|
|
|
735
760
|
// Fallback: flat output in outDir
|
|
761
|
+
const outputName = basename(file.path).replace(/\.cnx$|\.cnext$/, ext);
|
|
736
762
|
return join(this.config.outDir, outputName);
|
|
737
763
|
}
|
|
738
764
|
|
|
@@ -816,37 +842,25 @@ class Pipeline {
|
|
|
816
842
|
* Uses headerOutDir if specified, otherwise falls back to outDir
|
|
817
843
|
*/
|
|
818
844
|
private getHeaderOutputPath(file: IDiscoveredFile): string {
|
|
819
|
-
const headerName = basename(file.path).replace(/\.cnx$|\.cnext$/, ".h");
|
|
820
|
-
|
|
821
845
|
// Use headerOutDir if specified, otherwise fall back to outDir
|
|
822
846
|
const headerDir = this.config.headerOutDir || this.config.outDir;
|
|
823
847
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
848
|
+
const relativePath = this.getRelativePathFromInputs(file.path);
|
|
849
|
+
if (relativePath) {
|
|
850
|
+
// File is under an input directory - preserve structure
|
|
851
|
+
const outputRelative = relativePath.replace(/\.cnx$|\.cnext$/, ".h");
|
|
852
|
+
const outputPath = join(headerDir, outputRelative);
|
|
827
853
|
|
|
828
|
-
|
|
829
|
-
if (existsSync(
|
|
830
|
-
|
|
854
|
+
const outputDir = dirname(outputPath);
|
|
855
|
+
if (!existsSync(outputDir)) {
|
|
856
|
+
mkdirSync(outputDir, { recursive: true });
|
|
831
857
|
}
|
|
832
858
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
// Check if file is under this input directory
|
|
836
|
-
if (relativePath && !relativePath.startsWith("..")) {
|
|
837
|
-
const outputRelative = relativePath.replace(/\.cnx$|\.cnext$/, ".h");
|
|
838
|
-
const outputPath = join(headerDir, outputRelative);
|
|
839
|
-
|
|
840
|
-
const outputDir = dirname(outputPath);
|
|
841
|
-
if (!existsSync(outputDir)) {
|
|
842
|
-
mkdirSync(outputDir, { recursive: true });
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
return outputPath;
|
|
846
|
-
}
|
|
859
|
+
return outputPath;
|
|
847
860
|
}
|
|
848
861
|
|
|
849
862
|
// Fallback: flat output in headerDir
|
|
863
|
+
const headerName = basename(file.path).replace(/\.cnx$|\.cnext$/, ".h");
|
|
850
864
|
return join(headerDir, headerName);
|
|
851
865
|
}
|
|
852
866
|
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
CPP14Parser,
|
|
10
|
+
ClassSpecifierContext,
|
|
11
|
+
} from "../parser/cpp/grammar/CPP14Parser";
|
|
9
12
|
import ISymbol from "../types/ISymbol";
|
|
10
13
|
import ESymbolKind from "../types/ESymbolKind";
|
|
11
14
|
import ESourceLanguage from "../types/ESourceLanguage";
|
|
@@ -187,13 +190,28 @@ class CppSymbolCollector {
|
|
|
187
190
|
|
|
188
191
|
const baseType = this.extractTypeFromDeclSpecSeq(declSpecSeq);
|
|
189
192
|
|
|
193
|
+
// Issue #342: Track anonymous class specifiers for typedef handling
|
|
194
|
+
let anonymousClassSpec: ClassSpecifierContext | null = null;
|
|
195
|
+
|
|
190
196
|
// Check for class specifier
|
|
191
197
|
for (const spec of declSpecSeq.declSpecifier?.() ?? []) {
|
|
192
198
|
const typeSpec = spec.typeSpecifier?.();
|
|
193
199
|
if (typeSpec) {
|
|
194
200
|
const classSpec = typeSpec.classSpecifier?.();
|
|
195
201
|
if (classSpec) {
|
|
196
|
-
this
|
|
202
|
+
// Check if this is a named struct/class
|
|
203
|
+
const classHead = classSpec.classHead?.();
|
|
204
|
+
const classHeadName = classHead?.classHeadName?.();
|
|
205
|
+
const className = classHeadName?.className?.();
|
|
206
|
+
const identifier = className?.Identifier?.();
|
|
207
|
+
|
|
208
|
+
if (identifier?.getText()) {
|
|
209
|
+
// Named struct - collect normally
|
|
210
|
+
this.collectClassSpecifier(classSpec, line);
|
|
211
|
+
} else {
|
|
212
|
+
// Issue #342: Anonymous struct - save for typedef handling below
|
|
213
|
+
anonymousClassSpec = classSpec;
|
|
214
|
+
}
|
|
197
215
|
}
|
|
198
216
|
|
|
199
217
|
const enumSpec = typeSpec.enumSpecifier?.();
|
|
@@ -218,6 +236,27 @@ class CppSymbolCollector {
|
|
|
218
236
|
? `${this.currentNamespace}::${name}`
|
|
219
237
|
: name;
|
|
220
238
|
|
|
239
|
+
// Issue #342: If we have an anonymous struct and this is a typedef,
|
|
240
|
+
// collect struct fields using the typedef name
|
|
241
|
+
if (anonymousClassSpec && this.symbolTable) {
|
|
242
|
+
const memberSpec = anonymousClassSpec.memberSpecification?.();
|
|
243
|
+
if (memberSpec) {
|
|
244
|
+
// Add the type symbol
|
|
245
|
+
this.symbols.push({
|
|
246
|
+
name: fullName,
|
|
247
|
+
kind: ESymbolKind.Class, // Treat typedef'd structs as classes
|
|
248
|
+
sourceFile: this.sourceFile,
|
|
249
|
+
sourceLine: line,
|
|
250
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
251
|
+
isExported: true,
|
|
252
|
+
parent: this.currentNamespace,
|
|
253
|
+
});
|
|
254
|
+
// Collect members using the typedef name
|
|
255
|
+
this.collectClassMembers(fullName, memberSpec);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
221
260
|
// Issue #322: Extract parameters for function declarations
|
|
222
261
|
const params = isFunction
|
|
223
262
|
? this.extractFunctionParameters(declarator)
|