Skip to content

Commit 8136dc1

Browse files
committed
docs: add ADR-15 C# preprocessor block attribute detection limitation
Document tree-sitter-c-sharp grammar limitation where preprocessor directives between attributes are parsed as ERROR nodes. - Add Core ADR-15 document (EN/KO) - Update 6 index files (homepage, adr/index, adr/core/index) - Add VitePress sidebar configuration
1 parent 0347229 commit 8136dc1

File tree

9 files changed

+392
-64
lines changed

9 files changed

+392
-64
lines changed

docs/.vitepress/config.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export default defineConfig({
111111
{ text: "Parallel Scanning", link: "/en/adr/core/12-parallel-scanning-worker-pool" },
112112
{ text: "NaCl SecretBox Encryption", link: "/en/adr/core/13-nacl-secretbox-encryption" },
113113
{ text: "Indirect Import Unsupported", link: "/en/adr/core/14-indirect-import-unsupported" },
114+
{ text: "C# Preprocessor Limitation", link: "/en/adr/core/15-csharp-preprocessor-attribute-limitation" },
114115
],
115116
},
116117
{
@@ -247,6 +248,7 @@ export default defineConfig({
247248
{ text: "병렬 스캐닝", link: "/ko/adr/core/12-parallel-scanning-worker-pool" },
248249
{ text: "NaCl SecretBox 암호화", link: "/ko/adr/core/13-nacl-secretbox-encryption" },
249250
{ text: "간접 Import 미지원", link: "/ko/adr/core/14-indirect-import-unsupported" },
251+
{ text: "C# 전처리기 한계", link: "/ko/adr/core/15-csharp-preprocessor-attribute-limitation" },
250252
],
251253
},
252254
{
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
---
2+
title: C# Preprocessor Block Attribute Detection Limitation
3+
description: ADR for attribute detection limitation inside conditional compilation blocks due to tree-sitter-c-sharp grammar
4+
---
5+
6+
# ADR-15: C# Preprocessor Block Attribute Detection Limitation
7+
8+
> 🇰🇷 [Korean Version](/ko/adr/core/15-csharp-preprocessor-attribute-limitation.md)
9+
10+
| Date | Author | Repository |
11+
| ---------- | ------------ | ---------- |
12+
| 2026-01-04 | @KubrickCode | core |
13+
14+
**Status**: Accepted
15+
16+
## Background
17+
18+
### Problem Definition
19+
20+
The tree-sitter-c-sharp grammar parses preprocessor directives (`#if`, `#else`, `#elif`) between attributes as `ERROR` nodes instead of `preproc_if` nodes.
21+
22+
### Discovery
23+
24+
Validation of `fluentassertions/fluentassertions` repository:
25+
26+
- Ground Truth (AI Manual Analysis): 5,995 tests
27+
- Parser Result: 6,009 tests
28+
- Delta: +14 (+0.23%)
29+
30+
The delta is positive because GT analysis errors outnumber parser bugs. The actual parser bug caused 2 tests to be missed in `AssertionExtensionsSpecs.cs`.
31+
32+
### Technical Analysis
33+
34+
```csharp
35+
// InlineData(2) is not detected in this pattern
36+
[Theory]
37+
[InlineData(1)]
38+
#if NET6_0_OR_GREATER
39+
[InlineData(2)] // ← Parser misses this
40+
#endif
41+
public void Test(int x) { }
42+
```
43+
44+
Actual tree-sitter parsing result:
45+
46+
```
47+
method_declaration
48+
├── attribute_list [Theory]
49+
├── attribute_list [InlineData(1)]
50+
├── ERROR ← Not preproc_if!
51+
│ └── #if NET6_0_OR_GREATER
52+
│ └── (InlineData(2) incorrectly parsed)
53+
└── public void Test()
54+
```
55+
56+
**Note**: Class-level `#if` (wrapping entire methods) works correctly:
57+
58+
```csharp
59+
// This pattern is detected correctly
60+
#if NET6_0_OR_GREATER
61+
[Fact]
62+
public void Net6OnlyTest() { }
63+
#endif
64+
```
65+
66+
## Decision
67+
68+
**Test attribute detection inside preprocessor blocks between attributes is not supported.**
69+
70+
This is a tree-sitter-c-sharp grammar-level issue that cannot be fixed in the SpecVital Core parser.
71+
72+
### Rationale
73+
74+
1. **Grammar-level limitation**: tree-sitter-c-sharp generates incorrect AST, making parser-level workaround impossible
75+
2. **Upstream dependency**: Fix requires modifying the tree-sitter-c-sharp grammar itself
76+
3. **Limited impact**: Most C# projects don't use preprocessors between attributes
77+
78+
## Options Considered
79+
80+
### Option A: Accept Limitation and Document (Selected)
81+
82+
Document the limitation and verify behavior with tests.
83+
84+
**Pros:**
85+
86+
- Honest representation of limitations
87+
- Automatically resolved if tree-sitter-c-sharp is fixed
88+
89+
**Cons:**
90+
91+
- Test under-count in certain codebases
92+
93+
### Option B: Text-based Preprocessor Expansion
94+
95+
Process preprocessor directives at text level before AST parsing.
96+
97+
**Pros:**
98+
99+
- Can detect attributes inside preprocessor blocks
100+
101+
**Cons:**
102+
103+
- **Complexity explosion**: Requires condition evaluation, nesting handling, multiple branch processing
104+
- **Accuracy degradation**: Cannot know which conditions are active
105+
- **Architecture violation**: Conflicts with tree-sitter-based parsing principles
106+
107+
### Option C: Fork tree-sitter-c-sharp
108+
109+
Directly modify the grammar to support preprocessors between attributes.
110+
111+
**Pros:**
112+
113+
- Fundamental solution
114+
115+
**Cons:**
116+
117+
- **Maintenance burden**: Must continuously merge upstream changes
118+
- **Scope creep**: Forking entire grammar for single issue
119+
- **Uncertainty**: Difficult to predict side effects of grammar modification
120+
121+
## Consequences
122+
123+
### Positive
124+
125+
1. **Architecture integrity**: Maintains tree-sitter-based parsing model
126+
2. **Clear limitations**: Documented in code comments and tests
127+
3. **Maintainability**: No complex workarounds
128+
129+
### Negative
130+
131+
1. **Accuracy gap**: Projects using preprocessors between attributes will have under-counted tests
132+
2. **FluentAssertions impact**: Under-count due to `[InlineData]` usage in `#if` blocks
133+
134+
### Mitigation
135+
136+
1. **Minimal impact**: Most projects don't use this pattern
137+
2. **Class-level works**: `#if` wrapping entire methods works correctly
138+
3. **Documentation**: Limitation documented in `GetAttributeLists()` function comment
139+
140+
## Framework Impact
141+
142+
| Framework | Affected Pattern | Severity |
143+
| --------- | ----------------------- | -------- |
144+
| xUnit | `[InlineData]` in `#if` | Low |
145+
| NUnit | `[TestCase]` in `#if` | Low |
146+
| MSTest | `[DataRow]` in `#if` | Low |
147+
148+
Most C# test projects use class-level or method-level conditional compilation. The pattern of inserting preprocessors between attributes is rare.
149+
150+
## Related ADRs
151+
152+
- [ADR-02: Dynamic Test Counting Policy](./02-dynamic-test-counting-policy.md) - Another accuracy limitation
153+
- [ADR-03: Tree-sitter AST Parsing Engine](./03-tree-sitter-ast-parsing-engine.md) - Tree-sitter-based parsing principles
154+
- [ADR-14: Indirect Import Alias Detection Unsupported](./14-indirect-import-unsupported.md) - Similar limitation documentation pattern
155+
156+
## References
157+
158+
- [tree-sitter-c-sharp GitHub](https://github.com/tree-sitter/tree-sitter-c-sharp)
159+
- Validation Report: `realworld-test-report.md`
160+
- Limitation Test: `pkg/parser/strategies/shared/dotnetast/ast_test.go:TestGetAttributeLists_PreprocessorLimitation`

docs/en/adr/core/index.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,23 @@ Architecture Decision Records for the [specvital/core](https://github.com/specvi
1111

1212
## ADR Index
1313

14-
| # | Title | Date |
15-
| --- | ----------------------------------------------------------------------------------------- | ---------- |
16-
| 01 | [Core Library Separation](./01-core-library-separation.md) | 2024-12-17 |
17-
| 02 | [Dynamic Test Counting Policy](./02-dynamic-test-counting-policy.md) | 2024-12-22 |
18-
| 03 | [Tree-sitter as AST Parsing Engine](./03-tree-sitter-ast-parsing-engine.md) | 2024-12-23 |
19-
| 04 | [Early-Return Framework Detection](./04-early-return-framework-detection.md) | 2024-12-23 |
20-
| 05 | [Parser Pooling Disabled](./05-parser-pooling-disabled.md) | 2024-12-23 |
21-
| 06 | [Unified Framework Definition](./06-unified-framework-definition.md) | 2024-12-23 |
22-
| 07 | [Source Abstraction Interface](./07-source-abstraction-interface.md) | 2024-12-23 |
23-
| 08 | [Shared Parser Modules](./08-shared-parser-modules.md) | 2024-12-23 |
24-
| 09 | [Config Scope Resolution](./09-config-scope-resolution.md) | 2024-12-23 |
25-
| 10 | [Standard Go Project Layout](./10-standard-go-project-layout.md) | 2024-12-23 |
26-
| 11 | [Integration Testing with Golden Snapshots](./11-integration-testing-golden-snapshots.md) | 2024-12-23 |
27-
| 12 | [Parallel Scanning with Worker Pool](./12-parallel-scanning-worker-pool.md) | 2024-12-23 |
28-
| 13 | [NaCl SecretBox Encryption](./13-nacl-secretbox-encryption.md) | 2024-12-23 |
29-
| 14 | [Indirect Import Alias Detection Unsupported](./14-indirect-import-unsupported.md) | 2025-12-29 |
14+
| # | Title | Date |
15+
| --- | -------------------------------------------------------------------------------------------------------- | ---------- |
16+
| 01 | [Core Library Separation](./01-core-library-separation.md) | 2024-12-17 |
17+
| 02 | [Dynamic Test Counting Policy](./02-dynamic-test-counting-policy.md) | 2024-12-22 |
18+
| 03 | [Tree-sitter as AST Parsing Engine](./03-tree-sitter-ast-parsing-engine.md) | 2024-12-23 |
19+
| 04 | [Early-Return Framework Detection](./04-early-return-framework-detection.md) | 2024-12-23 |
20+
| 05 | [Parser Pooling Disabled](./05-parser-pooling-disabled.md) | 2024-12-23 |
21+
| 06 | [Unified Framework Definition](./06-unified-framework-definition.md) | 2024-12-23 |
22+
| 07 | [Source Abstraction Interface](./07-source-abstraction-interface.md) | 2024-12-23 |
23+
| 08 | [Shared Parser Modules](./08-shared-parser-modules.md) | 2024-12-23 |
24+
| 09 | [Config Scope Resolution](./09-config-scope-resolution.md) | 2024-12-23 |
25+
| 10 | [Standard Go Project Layout](./10-standard-go-project-layout.md) | 2024-12-23 |
26+
| 11 | [Integration Testing with Golden Snapshots](./11-integration-testing-golden-snapshots.md) | 2024-12-23 |
27+
| 12 | [Parallel Scanning with Worker Pool](./12-parallel-scanning-worker-pool.md) | 2024-12-23 |
28+
| 13 | [NaCl SecretBox Encryption](./13-nacl-secretbox-encryption.md) | 2024-12-23 |
29+
| 14 | [Indirect Import Alias Detection Unsupported](./14-indirect-import-unsupported.md) | 2025-12-29 |
30+
| 15 | [C# Preprocessor Block Attribute Detection Limitation](./15-csharp-preprocessor-attribute-limitation.md) | 2026-01-04 |
3031

3132
## Related
3233

docs/en/adr/index.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,23 @@ XX-brief-decision-title.md
7272

7373
### Core Repository
7474

75-
| # | Title | Area | Date |
76-
| --- | ---------------------------------------------------------------------------------------------- | ------- | ---------- |
77-
| 01 | [Core Library Separation](./core/01-core-library-separation.md) | Core | 2024-12-17 |
78-
| 02 | [Dynamic Test Counting Policy](./core/02-dynamic-test-counting-policy.md) | Core | 2024-12-22 |
79-
| 03 | [Tree-sitter as AST Parsing Engine](./core/03-tree-sitter-ast-parsing-engine.md) | Parser | 2024-12-23 |
80-
| 04 | [Early-Return Framework Detection](./core/04-early-return-framework-detection.md) | Parser | 2024-12-23 |
81-
| 05 | [Parser Pooling Disabled](./core/05-parser-pooling-disabled.md) | Parser | 2024-12-23 |
82-
| 06 | [Unified Framework Definition](./core/06-unified-framework-definition.md) | Parser | 2024-12-23 |
83-
| 07 | [Source Abstraction Interface](./core/07-source-abstraction-interface.md) | Parser | 2024-12-23 |
84-
| 08 | [Shared Parser Modules](./core/08-shared-parser-modules.md) | Parser | 2024-12-23 |
85-
| 09 | [Config Scope Resolution](./core/09-config-scope-resolution.md) | Config | 2024-12-23 |
86-
| 10 | [Standard Go Project Layout](./core/10-standard-go-project-layout.md) | Project | 2024-12-23 |
87-
| 11 | [Integration Testing with Golden Snapshots](./core/11-integration-testing-golden-snapshots.md) | Testing | 2024-12-23 |
88-
| 12 | [Parallel Scanning with Worker Pool](./core/12-parallel-scanning-worker-pool.md) | Perf | 2024-12-23 |
89-
| 13 | [NaCl SecretBox Encryption](./core/13-nacl-secretbox-encryption.md) | Crypto | 2024-12-23 |
90-
| 14 | [Indirect Import Alias Detection Unsupported](./core/14-indirect-import-unsupported.md) | Parser | 2025-12-29 |
75+
| # | Title | Area | Date |
76+
| --- | ------------------------------------------------------------------------------------------------------------- | ------- | ---------- |
77+
| 01 | [Core Library Separation](./core/01-core-library-separation.md) | Core | 2024-12-17 |
78+
| 02 | [Dynamic Test Counting Policy](./core/02-dynamic-test-counting-policy.md) | Core | 2024-12-22 |
79+
| 03 | [Tree-sitter as AST Parsing Engine](./core/03-tree-sitter-ast-parsing-engine.md) | Parser | 2024-12-23 |
80+
| 04 | [Early-Return Framework Detection](./core/04-early-return-framework-detection.md) | Parser | 2024-12-23 |
81+
| 05 | [Parser Pooling Disabled](./core/05-parser-pooling-disabled.md) | Parser | 2024-12-23 |
82+
| 06 | [Unified Framework Definition](./core/06-unified-framework-definition.md) | Parser | 2024-12-23 |
83+
| 07 | [Source Abstraction Interface](./core/07-source-abstraction-interface.md) | Parser | 2024-12-23 |
84+
| 08 | [Shared Parser Modules](./core/08-shared-parser-modules.md) | Parser | 2024-12-23 |
85+
| 09 | [Config Scope Resolution](./core/09-config-scope-resolution.md) | Config | 2024-12-23 |
86+
| 10 | [Standard Go Project Layout](./core/10-standard-go-project-layout.md) | Project | 2024-12-23 |
87+
| 11 | [Integration Testing with Golden Snapshots](./core/11-integration-testing-golden-snapshots.md) | Testing | 2024-12-23 |
88+
| 12 | [Parallel Scanning with Worker Pool](./core/12-parallel-scanning-worker-pool.md) | Perf | 2024-12-23 |
89+
| 13 | [NaCl SecretBox Encryption](./core/13-nacl-secretbox-encryption.md) | Crypto | 2024-12-23 |
90+
| 14 | [Indirect Import Alias Detection Unsupported](./core/14-indirect-import-unsupported.md) | Parser | 2025-12-29 |
91+
| 15 | [C# Preprocessor Block Attribute Detection Limitation](./core/15-csharp-preprocessor-attribute-limitation.md) | Parser | 2026-01-04 |
9192

9293
### Collector Repository
9394

docs/en/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Documentation of architectural decisions made during the development of Specvita
5959
- [Parallel Scanning with Worker Pool](./adr/core/12-parallel-scanning-worker-pool.md)
6060
- [NaCl SecretBox Encryption](./adr/core/13-nacl-secretbox-encryption.md)
6161
- [Indirect Import Alias Detection Unsupported](./adr/core/14-indirect-import-unsupported.md)
62+
- [C# Preprocessor Block Attribute Detection Limitation](./adr/core/15-csharp-preprocessor-attribute-limitation.md)
6263

6364
**[Collector](./adr/collector/)**
6465

0 commit comments

Comments
 (0)