Reference

05. 언어 지원 전략

언어별 파싱/분석 방식, 경량 렉서/Go AST 경로, 지원 우선순위

Source: docs/05-language-support.md

05. 언어 지원 전략

현재 구현 상태 (v0.46.0 / metrics v14.5.0): 6개 정식 지원 언어(TS/JS, Python, Go, Rust, Java, C#)와 C/C++ 제한 지원은 모두 internal/lang경량 렉서 프론트엔드(퍼지 파싱, lizard 방식) 또는 표준 Go AST 경로로 동작한다. C/C++은 전처리기 미전개 한계 때문에 confidence는 medium 중심이며, SIZE-02/03, CPLX-01~05, ARCH-01/02/03(include 그래프), DUP-01/02, SEC-01, HYG-01/03/04/05/06/08, RES-01(malloc/free·new/delete)을 지원한다. Rust ARCH-01/03은 mod.rs facade/re-export 관례를 반영해 부모/자식 모듈 계층 엣지를 제외하고 sibling/cross-branch 의존만 순환·결합 후보로 본다. Go 파일은 추가로 표준 라이브러리 go/parser 파스 실패를 HYG-07로 기록하고 --strict에서 exit 3을 반환한다. 렉서가 보지 못하는 타입 내부 구조(클래스 필드·메서드·상속)가 필요한 지표(ARCH-07 LCOM4, SIZE-04 god type, ARCH-08 DIT)는 추가로 tree-sitter WASM(wasm32-wasi + 순수 Go wazero, CGO_ENABLED=0 유지) 프론트엔드를 사용한다 — §3 참조. v13 점수는 아래 §4.1의 카테고리×언어 eligible production CLOC만 분모로 쓴다.

1. 지원 언어 (확정)

정식 지원 6개 언어: TypeScript/JavaScript, Python, Go, Rust, Java, C#. 추가로 C/C++은 제한 지원(§5)한다. 6개 언어는 전체 메트릭 적용이 목표이고, C/C++은 빌드 없는 정적 스캔에서 신뢰 가능한 범위만 적용한다.

단계언어파서비고
v0.1 (구현 완료)TypeScript / JavaScript (+JSX/TSX)내부 렉서 (tree-sitter 이연)AI 산출물 최다 언어
v0.1 (구현 완료)Python내부 렉서 (tree-sitter 이연)
v0.1 (구현 완료)Gogo/ast (stdlib)자체 도구 언어, 무료로 고정밀. 수직 슬라이스 기준 언어
v0.4 (구현 완료)Java, C#내부 렉서 (tree-sitter 이연)기업 환경 수요
v0.4 (구현 완료)Rust내부 렉서 (tree-sitter 이연)소유권 모델로 RES 일부가 불필요 — 대신 Rust 고유 메트릭(§2.4)
v0.7 (구현 완료)C / C++내부 렉서전처리기/매크로 한계로 일부 메트릭 제한 — §5

언어 미지원 파일도 텍스트 수준 메트릭은 동작한다: SIZE-01(라인 카운트, 휴리스틱 주석 인식), SEC-01(시크릿 — 정규식/엔트로피는 언어 무관), HYG-03(TODO). 따라서 어떤 프로젝트든 부분 진단은 가능하다. 다만 v13 점수에서는 명시적 지원표 밖 finding을 unsupported_language로 보존·제외해, 부분 진단을 완전한 언어 커버리지처럼 점수화하지 않는다. other production CLOC는 언어 독립 SIZE만 eligible이다.

2. 언어별 Spec 작성 가이드

새 언어 추가 = internal/lang/<lang> 프론트엔드 1개 + 엔진 blank import + 픽스처 테스트. 목표 작업량: 언어당 2~3일.

2.1 매핑 테이블에 반드시 정의할 것

항목예시 (Python)예시 (TypeScript)
함수 추출def, lambda 근사function/method/arrow 추출
분기 토큰(CCN)if, elif, for, while, except, and, orif, for, while, case, catch, &&, ||, ?:
중첩 추적들여쓰기/블록 토큰{} 블록과 제어문
주석/문자열 처리#, triple quote//, /* */, string/template literal
import 추출import, from ... importimport, require(), re-export
한계 문서화동적 import, decorator 영향JSX/TS 타입 구문, regex/template literal 휴리스틱

2.2 import → 내부 모듈 해석 (빌드 없이)

ARCH 메트릭의 정밀도를 좌우하는 부분. 언어별 해석 규칙:

언어모듈 단위내부/외부 판별해석 규칙
Go패키지(디렉터리)go.mod의 module path 접두 일치import path → 디렉터리 직매핑
TS/JS파일상대 경로(./, ../) 또는 tsconfig.json/package.json의 paths/alias확장자 보완(.ts, .tsx, /index.ts), alias는 tsconfig paths 파싱
Python파일/패키지프로젝트 루트 기준 최상위 패키지명 일치dotted path → 디렉터리/파일, __init__.py 인식, 상대 import 처리
Java패키지소스 트리 내 동일 package 선언 존재 여부package 선언으로 역인덱스 구축
C#네임스페이스소스 트리 내 동일 namespace 선언 존재 여부using → namespace 역인덱스. .csproj/.sln으로 프로젝트 경계 인식
Rust모듈(mod)crate 내부 경로(crate::, self::, super::)use 선언 → mod 트리/파일 매핑. Cargo.toml workspace로 crate 간 의존 인식
C/C++파일#include "..."(따옴표)만 내부 후보include 경로 휴리스틱(루트, 같은 디렉터리, include/)

해석 실패한 import는 외부로 간주(감점 없음). 오탐(가짜 순환)을 만들지 않는 방향으로 보수적으로.

순환 검출 단위는 언어 관례를 따른다: Go/Java/Python은 패키지 수준, TS/JS는 파일 수준(파일=모듈), C/C++은 헤더 포함 그래프.

2.3 SEC/RES 룰의 언어별 인스턴스

각 룰은 "언어 무관 정의(CWE) + 언어별 패턴"으로 분리한다. 예: SEC-03 TLS 검증 비활성화

언어패턴
Gotls.Config{InsecureSkipVerify: true}
Pythonverify=False (requests 계열 호출 인자), ssl._create_unverified_context
JS/TSrejectUnauthorized: false, NODE_TLS_REJECT_UNAUTHORIZED=0
JavaTrustManager no-op 구현, setHostnameVerifier(ALLOW_ALL)
C#ServerCertificateCustomValidationCallback가 항상 true 반환, ServicePointManager.ServerCertificateValidationCallback 무조건 통과
Rustreqwest danger_accept_invalid_certs(true), native-tls danger_accept_invalid_hostnames

현재 룰은 Go 코드와 경량 lexer 패턴으로 구현한다. 후속 플러그인 모델에서는 YAML 메타데이터 또는 tree-sitter 쿼리를 사용할 수 있다.

2.4 언어 고유 메트릭 보강

언어별로 "그 언어에서 위험 신호인 것"을 추가 룰로 둔다(메트릭 ID는 기존 카테고리에 귀속):

언어보강 룰귀속
Rustunsafe 블록 밀도, unwrap()/expect() 남용(라이브러리 코드), mem::forget/Box::leak, #[allow(...)] 남발RES/HYG
C#IDisposableusing 없이 사용, async void(이벤트 핸들러 외), .Result/.Wait() 블로킹(데드락 위험)RES/HYG
Javatry-with-resources 미사용 Closeable, 빈 InterruptedException catch, raw type 사용RES/HYG
Go고루틴 누수 패턴(취소 없는 for-select), context 미전파RES
Pythonmutable default argument, bare except:HYG
TS/JSany 밀도(TS), floating promise(await 누락 휴리스틱)HYG

3. tree-sitter WASM 통합 (구현됨)

기본(렉서) 프론트엔드 외에, 타입 내부 구조가 필요한 지표는 tree-sitter를 사용한다(v8.2.0~). 문법을 wasm32-wasi로 컴파일해 순수 Go wazero 런타임으로 구동하므로 CGO_ENABLED=0 정적 빌드가 유지된다(emscripten·CGo go-tree-sitter 불필요). 현재 ARCH-07(LCOM4 응집도, Go+TS), SIZE-04(God Type, Go=go/ast / TS·Java·Python·C#·C++·Rust=tree-sitter), ARCH-08(DIT, TS·Java·C#·C++), 그리고 SEC-07/08의 함수 내 taint 분석(TS/JS 소스→싱크 데이터플로우)이 이 경로를 쓴다. Rust는 메서드가 struct 본문이 아니라 별도 impl 블록에 있어 inherent impl을 타입명으로 합산하는 전용 추출기를 쓴다(trait impl 제외, 상속 없어 DIT N/A). 문법 *.wasm은 gzip 압축해 go:embed(첫 컴파일 시 1회 gunzip). 설계 메모:

  1. 빌드 파이프라인: 문법 C 소스(parser.c+scanner.c)와 작은 래퍼(wrap_generic.c)를 wasi-sdk clang으로 wasm32-wasi 타깃 컴파일(emscripten 미사용) → gzip 압축 → go:embed. grammars/build.sh가 언어 목록을 돌며 재생성한다.
  2. 런타임: wazero(순수 Go, CGo 없음)로 인스턴스화. WASI command 모듈이라 파일당 1회 인스턴스화(컴파일은 grammar별 1회 캐시).
  3. 노드 추출: 래퍼가 named 노드를 id/parent/start/end/field/type TSV로 stdout에 출력 → Go에서 트리 재구성. 언어별 추출(클래스/메서드/필드/상속)은 Go에서 처리.
  4. 에러 회복: tree-sitter는 문법 오류가 있어도 ERROR 노드를 포함한 트리를 반환 → 부분 추출 가능.
  5. 검증: 픽스처의 노드 종류 단위 테스트(TestGodClasses_*)로 문법 업그레이드 시 매핑 호환성 확인.

폴백 계획

wazero 경유 성능이 목표(100k LOC/30s)에 못 미치면: (a) 토큰 수준 메트릭(SIZE, DUP, SEC-01)은 자체 경량 렉서로 분리, (b) 빌드 태그로 CGo 버전을 옵션 제공(기본은 순수 Go 유지).

4. 언어별 메트릭 적용 매트릭스

메트릭GoTS/JSPythonJavaC#RustC/C++(제한)미지원 언어
SIZE-01✓(텍스트)
SIZE-02/03
SIZE-04
CPLX-01/02/03✓(match 분기 포함)△(매크로 영향)
CPLX-04/05
ARCH-01/02/03✓(패키지/파일)△(파일/심볼)△(패키지/심볼)△(패키지/심볼)△(네임스페이스/심볼)△(mod/crate/심볼)△(include/심볼)
DUP-01/02△(라인 해시)
SEC-01✓(정규식)
SEC-02~05△(싱크 적음)
RES-01✓(defer)✓(with)△(RAII 예외: forget/leak/unsafe)✓(malloc/free·new/delete)
HYG-01✓(err 무시)✓(빈 catch)✓(except: pass)✓(빈 catch)✓(unwrap 남용, let _ =)
HYG-02/03/06△(03만)
HYG-04/05✓(API doc 포함)
HYG-08(주석코드)
HYG-09(미구현 스텁)✓(panic/errors)✓(throw)△(문구형만)✓(todo!)
HYG-10(디버그 잔재)✗(전용 호출 없음)✓(console.debug 등)✓(breakpoint/pdb)✓(printStackTrace)✓(Debugger.Break)✓(dbg!)
HYG-11(명명 비일관)✗(대소문자 관례)
HYG-12(검사기 억제)✗(억제 관용구 없음)✓(@ts-ignore 등)✓(bare type:ignore)✓(@SuppressWarnings("all"))✓(pragma/nullable)✗(타입 억제 불가)

✓ 완전 지원, △ 부분/휴리스틱, ✗ 미적용. 이 표는 finding 생성의 세부 커버리지이고, 아래 표는 점수 분모의 보수적 eligibility 계약이다.

4.1 점수 분모용 카테고리×언어 매트릭스 (v13.0.0)

세부 룰별 부분 지원 정도는 위 표가 원본이고, 점수 엔진은 보수적인 카테고리 단위 지원표를 사용한다.

카테고리eligible production 언어미지원 CLOC 처리
ARCHGo, TS/JS, Python, Java, C#, Rust, C/C++분모 제외, finding은 unsupported_language
CPLX위와 같음
SEC위와 같음
DUP위와 같음
SIZE위 언어 + other 코드형 텍스트사실상 모든 분석 가능한 production 텍스트
RESGo, Python, Rust, C/C++TS/JS·Java·C# 등은 RES 분모 제외
HYGGo, TS/JS, Python, Java, C#, Rust, C/C++분모 제외, finding은 unsupported_language

coverage(category) = eligible production CLOC / all production CLOC. eligible CLOC가 0이면 그 카테고리는 not_assessed이며, 전 카테고리가 미측정이면 Grade N/A다. test/documentation/configuration/generated는 언어와 무관하게 모든 카테고리 점수 분모·debt에서 제외한다.

5. C/C++ 제한 지원의 범위와 이유

C/C++은 전처리기(#define, #ifdef, 매크로) 전개 없이는 구문 정보가 불완전하다는 본질적 문제가 있다. JAM은 빌드를 하지 않으므로(컴파일 플래그·include 경로를 모름) 전개 전 소스를 경량 lexer로 분석한다. 그 결과:

템플릿 메타프로그래밍이 무거운 현대 C++ 코드베이스는 정밀 분석이 컴파일러 기반 도구(clang-tidy 등)의 영역임을 10-coverage-and-limitations.md와 함께 명시한다.