typecheck: diagnose missing class names

This commit is contained in:
Hermes Agent
2026-09-17 06:25:04 +00:00
parent 649b327f52
commit b0dbe976de
7 changed files with 16 additions and 2 deletions
+5
View File
@@ -1,5 +1,10 @@
# Changelog # Changelog
### Diagnose missing class names
- `claro typecheck` now explains how to repair a bare `CLASS` declaration instead of silently accepting a class with no name.
- Added focused coverage to the complete typecheck diagnostic validation matrix.
### Diagnose missing function names ### Diagnose missing function names
- `claro typecheck` now explains how to repair a bare `TEACH` declaration instead of silently accepting a function with no name. - `claro typecheck` now explains how to repair a bare `TEACH` declaration instead of silently accepting a function with no name.
+1 -1
View File
@@ -191,7 +191,7 @@ Object methods must also have unique names within their class. If `CLASS Player`
Class fields must also have unique names and known types. If `CLASS Player` declares `HAS score NUMBER` twice, or declares the same field with another type, `claro typecheck` reports `Class Player declares field score more than once. Give each field a different name.` If a field omits its type, it reports `Class Player field score is missing a type. Add a type such as NUMBER, TEXT, YESNO, LIST, or MAP after the field name.` If a field uses an unknown type such as `BANANA`, it reports `Class Player field score uses an unknown type BANANA. Use a Claro type such as NUMBER, TEXT, YESNO, LIST, or MAP.` If extra text follows a valid type, it reports `Class Player field score has extra text after type NUMBER. Keep only the field name and one type.` Keep one `HAS` line for each field and use one supported type. Class fields must also have unique names and known types. If `CLASS Player` declares `HAS score NUMBER` twice, or declares the same field with another type, `claro typecheck` reports `Class Player declares field score more than once. Give each field a different name.` If a field omits its type, it reports `Class Player field score is missing a type. Add a type such as NUMBER, TEXT, YESNO, LIST, or MAP after the field name.` If a field uses an unknown type such as `BANANA`, it reports `Class Player field score uses an unknown type BANANA. Use a Claro type such as NUMBER, TEXT, YESNO, LIST, or MAP.` If extra text follows a valid type, it reports `Class Player field score has extra text after type NUMBER. Keep only the field name and one type.` Keep one `HAS` line for each field and use one supported type.
Class names must also be unique and use one name only. If a file declares `CLASS Player` twice, `claro typecheck` reports `Class Player is declared more than once. Give each class a different name.` If extra words follow the name, it reports `Class Player has extra text after its name. Keep only the class name after CLASS.` Rename or simplify the declaration before creating its objects. Object names must also be unique within a script: creating `NEW Player player` twice reports `Object player is created more than once. Give each object a different name.` Use a different object name for the second instance. Class declarations must include a name. A bare `CLASS` reports `CLASS needs a class name. Add a name after CLASS, such as CLASS Player.` Class names must also be unique and use one name only. If a file declares `CLASS Player` twice, `claro typecheck` reports `Class Player is declared more than once. Give each class a different name.` If extra words follow the name, it reports `Class Player has extra text after its name. Keep only the class name after CLASS.` Rename or simplify the declaration before creating its objects. Object names must also be unique within a script: creating `NEW Player player` twice reports `Object player is created more than once. Give each object a different name.` Use a different object name for the second instance.
Extra words after an object name are also rejected, for example `NEW Player player extra` reports `NEW Player has extra text after object name player. Keep only the class and object names.` Keep each `NEW` declaration to one class name and one object name. Extra words after an object name are also rejected, for example `NEW Player player extra` reports `NEW Player has extra text after object name player. Keep only the class and object names.` Keep each `NEW` declaration to one class name and one object name.
Simple functions and object methods can also declare a return type. `claro typecheck` checks each `RETURN` expression against it and reports a missing return when a declaration never returns a value: Simple functions and object methods can also declare a return type. `claro typecheck` checks each `RETURN` expression against it and reports a missing return when a declaration never returns a value:
+1
View File
@@ -57,6 +57,7 @@ Ready now:
- a narrow function/method argument check: `CHECK TYPE parameter IS TYPE` inside a function or simple object method lets `claro typecheck` accept correct checked calls, catch mismatched `DO`, `CALL ... WITH`, `DO object.method ...`, and compatibility `CALL object.method WITH ...` arguments, including checked methods that appear after another method in the same class, report missing checked function and method arguments, report missing unchecked arguments for simple functions and simple object methods in both modern `DO` and compatibility `CALL ... WITH` forms, treat empty compatibility calls such as `CALL greet WITH` and `CALL player.rename WITH` as missing-argument mistakes, catch extra arguments to simple functions and checked methods even when the checked method appears after another method in the same class, catch modern `DO` and compatibility `CALL ... WITH` calls to undeclared simple functions, explain when `DO object.method ...` or compatibility `CALL object.method ...` happens before the object is created with `NEW` even if the method name is also wrong, and catch modern plus compatibility calls to undeclared object methods with a class-specific `TEACH` hint - a narrow function/method argument check: `CHECK TYPE parameter IS TYPE` inside a function or simple object method lets `claro typecheck` accept correct checked calls, catch mismatched `DO`, `CALL ... WITH`, `DO object.method ...`, and compatibility `CALL object.method WITH ...` arguments, including checked methods that appear after another method in the same class, report missing checked function and method arguments, report missing unchecked arguments for simple functions and simple object methods in both modern `DO` and compatibility `CALL ... WITH` forms, treat empty compatibility calls such as `CALL greet WITH` and `CALL player.rename WITH` as missing-argument mistakes, catch extra arguments to simple functions and checked methods even when the checked method appears after another method in the same class, catch modern `DO` and compatibility `CALL ... WITH` calls to undeclared simple functions, explain when `DO object.method ...` or compatibility `CALL object.method ...` happens before the object is created with `NEW` even if the method name is also wrong, and catch modern plus compatibility calls to undeclared object methods with a class-specific `TEACH` hint
- function and method declarations now reject repeated parameter names with a direct repair hint before calls are checked, including modern and compatibility `TAKES` / `LEARNED` functions and methods; object methods with the same name are also rejected within a class with a rename hint in both modern and compatibility method syntax - function and method declarations now reject repeated parameter names with a direct repair hint before calls are checked, including modern and compatibility `TAKES` / `LEARNED` functions and methods; object methods with the same name are also rejected within a class with a rename hint in both modern and compatibility method syntax
- top-level function declarations now reject repeated names with a direct repair hint, so two `TEACH greet` blocks cannot silently compete for one callable function; a missing function name after `TEACH` gets a direct beginner-facing repair hint - top-level function declarations now reject repeated names with a direct repair hint, so two `TEACH greet` blocks cannot silently compete for one callable function; a missing function name after `TEACH` gets a direct beginner-facing repair hint
- class declarations now reject a missing class name after `CLASS` with a direct beginner-facing repair hint
- class declarations now reject repeated field names within the same class with a direct repair hint before field types are used, so two `HAS score ...` lines cannot silently choose conflicting metadata; different classes may independently use the same field name - class declarations now reject repeated field names within the same class with a direct repair hint before field types are used, so two `HAS score ...` lines cannot silently choose conflicting metadata; different classes may independently use the same field name
- class field declarations now reject missing types such as `HAS score`, unknown types such as `HAS score BANANA`, and extra tokens such as `HAS score NUMBER TEXT` with the field name, class name, supported type examples, and a repair hint before object-field checks use that metadata - class field declarations now reject missing types such as `HAS score`, unknown types such as `HAS score BANANA`, and extra tokens such as `HAS score NUMBER TEXT` with the field name, class name, supported type examples, and a repair hint before object-field checks use that metadata
- class declarations now reject repeated class names with a direct repair hint, so two `CLASS Player` blocks cannot silently compete for the same name; extra words after a class name also get a direct repair hint instead of becoming part of the class identity - class declarations now reject repeated class names with a direct repair hint, so two `CLASS Player` blocks cannot silently compete for the same name; extra words after a class name also get a direct repair hint instead of becoming part of the class identity
+2
View File
@@ -65,6 +65,8 @@ Keep declared return types honest: `claro typecheck` now reports friendly diagno
8m. Keep class field metadata understandable: a missing or unknown type in a `HAS field TYPE` declaration, or extra text after a valid type, now gets a class-and-field diagnostic with supported type examples or a direct repair hint before object-field checks use that metadata. 8m. Keep class field metadata understandable: a missing or unknown type in a `HAS field TYPE` declaration, or extra text after a valid type, now gets a class-and-field diagnostic with supported type examples or a direct repair hint before object-field checks use that metadata.
8n. Keep object creation unambiguous: extra words after `NEW ClassName object` now get a direct repair hint, so a malformed object declaration cannot silently discard part of the learner's input. 8n. Keep object creation unambiguous: extra words after `NEW ClassName object` now get a direct repair hint, so a malformed object declaration cannot silently discard part of the learner's input.
8o. Keep class declarations understandable: a bare `CLASS` now gets a direct diagnostic explaining that a class name is required, with a small example repair.
### 1. Strong static types ### 1. Strong static types
Goal: make larger beginner programs safer without making first scripts harder. Goal: make larger beginner programs safer without making first scripts harder.
+1 -1
View File
@@ -625,7 +625,7 @@ static TypeFieldCheck *collect_class_field_type_checks(FILE *f){
return checks; return checks;
} }
static TypeMethodCheck *collect_class_method_type_checks(FILE *f){ TypeMethodCheck *checks=NULL; char line[4096]; char current_class[128]=""; while(fgets(line,sizeof(line),f)){ char tmp[4096],up[64],w[128]; char *t; strncpy(tmp,line,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(is_blank_or_comment(t)) continue; first_word(t,w,sizeof(w)); upper_copy(up,w,sizeof(up)); if(!strcmp(up,"CLASS")){ const char *pcur=t+5; char *cls=unquote_token(&pcur); snprintf(current_class,sizeof(current_class),"%s",trim_inplace(cls)); free(cls); continue; } if((!strcmp(up,"END")||!strcmp(up,"ENDCLASS"))&&current_class[0]){ current_class[0]=0; continue; } if(current_class[0]&&!strcmp(up,"TEACH")){ char teachline[4096]; char *fname=NULL; char **params=NULL; int pcnt=0; strncpy(teachline,t,sizeof(teachline)-1); teachline[sizeof(teachline)-1]=0; parse_teach_parts(teachline,&fname,&params,&pcnt); add_type_method_check(&checks,current_class,trim_inplace(fname)); while(fgets(line,sizeof(line),f)){ char inner[4096],iup[64],iw[128]; char *it; strncpy(inner,line,sizeof(inner)-1); inner[sizeof(inner)-1]=0; it=trim_inplace(inner); if(is_blank_or_comment(it)) continue; first_word(it,iw,sizeof(iw)); upper_copy(iup,iw,sizeof(iup)); if(!strcmp(iup,"END")||!strcmp(iup,"LEARNED")) break; } } } rewind(f); return checks; } static TypeMethodCheck *collect_class_method_type_checks(FILE *f){ TypeMethodCheck *checks=NULL; char line[4096]; char current_class[128]=""; while(fgets(line,sizeof(line),f)){ char tmp[4096],up[64],w[128]; char *t; strncpy(tmp,line,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(is_blank_or_comment(t)) continue; first_word(t,w,sizeof(w)); upper_copy(up,w,sizeof(up)); if(!strcmp(up,"CLASS")){ const char *pcur=t+5; char *cls=unquote_token(&pcur); snprintf(current_class,sizeof(current_class),"%s",trim_inplace(cls)); free(cls); continue; } if((!strcmp(up,"END")||!strcmp(up,"ENDCLASS"))&&current_class[0]){ current_class[0]=0; continue; } if(current_class[0]&&!strcmp(up,"TEACH")){ char teachline[4096]; char *fname=NULL; char **params=NULL; int pcnt=0; strncpy(teachline,t,sizeof(teachline)-1); teachline[sizeof(teachline)-1]=0; parse_teach_parts(teachline,&fname,&params,&pcnt); add_type_method_check(&checks,current_class,trim_inplace(fname)); while(fgets(line,sizeof(line),f)){ char inner[4096],iup[64],iw[128]; char *it; strncpy(inner,line,sizeof(inner)-1); inner[sizeof(inner)-1]=0; it=trim_inplace(inner); if(is_blank_or_comment(it)) continue; first_word(it,iw,sizeof(iw)); upper_copy(iup,iw,sizeof(iup)); if(!strcmp(iup,"END")||!strcmp(iup,"LEARNED")) break; } } } rewind(f); return checks; }
static int typecheck_file(const char *path){ FILE *f=fopen(path,"rb"); char line[4096]; int line_no=0,errs=0; Var *types=NULL; TypeParamCheck *param_checks=NULL; TypeFieldCheck *field_checks=NULL; TypeMethodCheck *method_checks=NULL; TypeArityCheck *arity_checks=NULL; TypeReturnCheck *return_checks=NULL; char current_func[256]=""; char current_method[256]=""; char current_class[128]=""; char declared_classes[128][256]; char declared_functions[128][256]; char declared_methods[128][256]; char declared_fields[128][256]; char declared_objects[128][256]; int declared_class_count=0,declared_function_count=0,declared_method_count=0,declared_field_count=0,declared_object_count=0; int in_teach=0,return_seen=0,top_level_return_seen=0,nested_blocks=0,conditional_paths_complete=1; int branch_returned[64],branch_has_else[64]; if(!f){ fprintf(stderr,"Could not open %s\n",path); return 1; } param_checks=collect_function_param_type_checks(f); arity_checks=collect_function_arity_checks(f); return_checks=collect_function_return_type_checks(f); field_checks=collect_class_field_type_checks(f); method_checks=collect_class_method_type_checks(f); type_env_set(&types,"RESULT","ANY"); type_env_set(&types,"LASTERROR","TEXT"); type_env_set(&types,"LASTERRORFILE","TEXT"); type_env_set(&types,"LASTERRORLINE","NUMBER"); type_env_set(&types,"LASTEXIT","NUMBER"); while(fgets(line,sizeof(line),f)){ char tmp[4096],up[64],w[128]; char *t; line_no++; strncpy(tmp,line,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(is_blank_or_comment(t)) continue; first_word(t,w,sizeof(w)); upper_copy(up,w,sizeof(up)); if(!strcmp(up,"CLASS")){ const char *pcur=t+5; char *cls=unquote_token(&pcur); char *name=trim_inplace(cls); char *extra=trim_inplace((char *)pcur); int duplicate=0,ci; for(ci=0;ci<declared_class_count;ci++) if(ci_eq(declared_classes[ci],name)){ duplicate=1; break; } if(*extra){ printf("%s:%d: Class %s has extra text after its name. Keep only the class name after CLASS.\n",path,line_no,name); errs++; } else if(duplicate){ printf("%s:%d: Class %s is declared more than once. Give each class a different name.\n",path,line_no,name); errs++; } else if(declared_class_count<128) snprintf(declared_classes[declared_class_count++],sizeof(declared_classes[0]),"%s",name); snprintf(current_class,sizeof(current_class),"%s",name); declared_field_count=0; free(cls); } static int typecheck_file(const char *path){ FILE *f=fopen(path,"rb"); char line[4096]; int line_no=0,errs=0; Var *types=NULL; TypeParamCheck *param_checks=NULL; TypeFieldCheck *field_checks=NULL; TypeMethodCheck *method_checks=NULL; TypeArityCheck *arity_checks=NULL; TypeReturnCheck *return_checks=NULL; char current_func[256]=""; char current_method[256]=""; char current_class[128]=""; char declared_classes[128][256]; char declared_functions[128][256]; char declared_methods[128][256]; char declared_fields[128][256]; char declared_objects[128][256]; int declared_class_count=0,declared_function_count=0,declared_method_count=0,declared_field_count=0,declared_object_count=0; int in_teach=0,return_seen=0,top_level_return_seen=0,nested_blocks=0,conditional_paths_complete=1; int branch_returned[64],branch_has_else[64]; if(!f){ fprintf(stderr,"Could not open %s\n",path); return 1; } param_checks=collect_function_param_type_checks(f); arity_checks=collect_function_arity_checks(f); return_checks=collect_function_return_type_checks(f); field_checks=collect_class_field_type_checks(f); method_checks=collect_class_method_type_checks(f); type_env_set(&types,"RESULT","ANY"); type_env_set(&types,"LASTERROR","TEXT"); type_env_set(&types,"LASTERRORFILE","TEXT"); type_env_set(&types,"LASTERRORLINE","NUMBER"); type_env_set(&types,"LASTEXIT","NUMBER"); while(fgets(line,sizeof(line),f)){ char tmp[4096],up[64],w[128]; char *t; line_no++; strncpy(tmp,line,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(is_blank_or_comment(t)) continue; first_word(t,w,sizeof(w)); upper_copy(up,w,sizeof(up)); if(!strcmp(up,"CLASS")){ const char *pcur=t+5; char *cls=unquote_token(&pcur); char *name=trim_inplace(cls); char *extra=trim_inplace((char *)pcur); int duplicate=0,ci; for(ci=0;ci<declared_class_count;ci++) if(ci_eq(declared_classes[ci],name)){ duplicate=1; break; } if(!*name){ printf("%s:%d: CLASS needs a class name. Add a name after CLASS, such as CLASS Player.\n",path,line_no); errs++; current_class[0]=0; } else if(*extra){ printf("%s:%d: Class %s has extra text after its name. Keep only the class name after CLASS.\n",path,line_no,name); errs++; } else if(duplicate){ printf("%s:%d: Class %s is declared more than once. Give each class a different name.\n",path,line_no,name); errs++; } else if(declared_class_count<128) snprintf(declared_classes[declared_class_count++],sizeof(declared_classes[0]),"%s",name); snprintf(current_class,sizeof(current_class),"%s",name); declared_field_count=0; free(cls); }
else if(current_class[0]&&!strcmp(up,"HAS")){ const char *pcur=t+3; char *field=unquote_token(&pcur); char *field_type=unquote_token(&pcur); char *name=trim_inplace(field); char *ft=trim_inplace(field_type); char *extra=trim_inplace((char *)pcur); int duplicate=0,fi; for(fi=0;fi<declared_field_count;fi++) if(ci_eq(declared_fields[fi],name)){ duplicate=1; break; } if(duplicate){ printf("%s:%d: Class %s declares field %s more than once. Give each field a different name.\n",path,line_no,current_class,name); errs++; } else if(!*ft){ printf("%s:%d: Class %s field %s is missing a type. Add a type such as NUMBER, TEXT, YESNO, LIST, or MAP after the field name.\n",path,line_no,current_class,name); errs++; } else if(!claro_is_type_word(ft)){ printf("%s:%d: Class %s field %s uses an unknown type %s. Use a Claro type such as NUMBER, TEXT, YESNO, LIST, or MAP.\n",path,line_no,current_class,name,ft); errs++; } else if(*extra){ printf("%s:%d: Class %s field %s has extra text after type %s. Keep only the field name and one type.\n",path,line_no,current_class,name,ft); errs++; } else if(declared_field_count<128) snprintf(declared_fields[declared_field_count++],sizeof(declared_fields[0]),"%s",name); free(field); free(field_type); } else if(current_class[0]&&!strcmp(up,"HAS")){ const char *pcur=t+3; char *field=unquote_token(&pcur); char *field_type=unquote_token(&pcur); char *name=trim_inplace(field); char *ft=trim_inplace(field_type); char *extra=trim_inplace((char *)pcur); int duplicate=0,fi; for(fi=0;fi<declared_field_count;fi++) if(ci_eq(declared_fields[fi],name)){ duplicate=1; break; } if(duplicate){ printf("%s:%d: Class %s declares field %s more than once. Give each field a different name.\n",path,line_no,current_class,name); errs++; } else if(!*ft){ printf("%s:%d: Class %s field %s is missing a type. Add a type such as NUMBER, TEXT, YESNO, LIST, or MAP after the field name.\n",path,line_no,current_class,name); errs++; } else if(!claro_is_type_word(ft)){ printf("%s:%d: Class %s field %s uses an unknown type %s. Use a Claro type such as NUMBER, TEXT, YESNO, LIST, or MAP.\n",path,line_no,current_class,name,ft); errs++; } else if(*extra){ printf("%s:%d: Class %s field %s has extra text after type %s. Keep only the field name and one type.\n",path,line_no,current_class,name,ft); errs++; } else if(declared_field_count<128) snprintf(declared_fields[declared_field_count++],sizeof(declared_fields[0]),"%s",name); free(field); free(field_type); }
else if(!strcmp(up,"TEACH")){ char teachline[4096],full_name[256],*fname=NULL,**params=NULL; int pcnt=0; int duplicate_method=0; strncpy(teachline,t,sizeof(teachline)-1); teachline[sizeof(teachline)-1]=0; parse_teach_parts(teachline,&fname,&params,&pcnt); if(!fname||!*trim_inplace(fname)){ printf("%s:%d: TEACH needs a function name. Add a name after TEACH, such as TEACH greet.\n",path,line_no); errs++; } if(current_class[0]) snprintf(full_name,sizeof(full_name),"%s.%s",current_class,fname); else snprintf(full_name,sizeof(full_name),"%s",fname); if(current_class[0]){ int mi; for(mi=0;mi<declared_method_count;mi++) if(ci_eq(declared_methods[mi],full_name)){ duplicate_method=1; break; } if(duplicate_method){ printf("%s:%d: Method %s is declared more than once. Give each method a different name.\n",path,line_no,full_name); errs++; } else if(declared_method_count<128) snprintf(declared_methods[declared_method_count++],sizeof(declared_methods[0]),"%s",full_name); } else { int fi,duplicate_function=0; for(fi=0;fi<declared_function_count;fi++) if(ci_eq(declared_functions[fi],full_name)){ duplicate_function=1; break; } if(duplicate_function){ printf("%s:%d: Function %s is declared more than once. Give each function a different name.\n",path,line_no,full_name); errs++; } else if(declared_function_count<128) snprintf(declared_functions[declared_function_count++],sizeof(declared_functions[0]),"%s",full_name); } { const char *duplicate=duplicate_param_name(params,pcnt); if(duplicate){ printf("%s:%d: %s %s declares parameter %s more than once. Give each parameter a different name.\n",path,line_no,current_class[0]?"Method":"Function",full_name,trim_inplace((char *)duplicate)); errs++; } } { int pi; for(pi=0;pi<pcnt;pi++) type_env_set(&types,params[pi],"ANY"); } current_func[0]=0; current_method[0]=0; return_seen=0; top_level_return_seen=0; nested_blocks=0; conditional_paths_complete=1; in_teach=1; if(current_class[0]) snprintf(current_method,sizeof(current_method),"%s",full_name); if(current_class[0]){ TypeFieldCheck *fc; for(fc=field_checks;fc;fc=fc->next) if(ci_eq(fc->cls,current_class)) type_env_set(&types,fc->field,fc->type); } { const char *declared_return=type_return_check_for(return_checks,full_name); if(declared_return&&!claro_is_type_word(declared_return)){ printf("%s:%d: %s %s declares an unknown return type %s. Use a Claro type such as NUMBER, TEXT, YESNO, LIST, or MAP.\n",path,line_no,strchr(full_name,'.')?"Method":"Function",full_name,declared_return); errs++; } else if(declared_return) snprintf(current_func,sizeof(current_func),"%s",full_name); } } else if(!strcmp(up,"TEACH")){ char teachline[4096],full_name[256],*fname=NULL,**params=NULL; int pcnt=0; int duplicate_method=0; strncpy(teachline,t,sizeof(teachline)-1); teachline[sizeof(teachline)-1]=0; parse_teach_parts(teachline,&fname,&params,&pcnt); if(!fname||!*trim_inplace(fname)){ printf("%s:%d: TEACH needs a function name. Add a name after TEACH, such as TEACH greet.\n",path,line_no); errs++; } if(current_class[0]) snprintf(full_name,sizeof(full_name),"%s.%s",current_class,fname); else snprintf(full_name,sizeof(full_name),"%s",fname); if(current_class[0]){ int mi; for(mi=0;mi<declared_method_count;mi++) if(ci_eq(declared_methods[mi],full_name)){ duplicate_method=1; break; } if(duplicate_method){ printf("%s:%d: Method %s is declared more than once. Give each method a different name.\n",path,line_no,full_name); errs++; } else if(declared_method_count<128) snprintf(declared_methods[declared_method_count++],sizeof(declared_methods[0]),"%s",full_name); } else { int fi,duplicate_function=0; for(fi=0;fi<declared_function_count;fi++) if(ci_eq(declared_functions[fi],full_name)){ duplicate_function=1; break; } if(duplicate_function){ printf("%s:%d: Function %s is declared more than once. Give each function a different name.\n",path,line_no,full_name); errs++; } else if(declared_function_count<128) snprintf(declared_functions[declared_function_count++],sizeof(declared_functions[0]),"%s",full_name); } { const char *duplicate=duplicate_param_name(params,pcnt); if(duplicate){ printf("%s:%d: %s %s declares parameter %s more than once. Give each parameter a different name.\n",path,line_no,current_class[0]?"Method":"Function",full_name,trim_inplace((char *)duplicate)); errs++; } } { int pi; for(pi=0;pi<pcnt;pi++) type_env_set(&types,params[pi],"ANY"); } current_func[0]=0; current_method[0]=0; return_seen=0; top_level_return_seen=0; nested_blocks=0; conditional_paths_complete=1; in_teach=1; if(current_class[0]) snprintf(current_method,sizeof(current_method),"%s",full_name); if(current_class[0]){ TypeFieldCheck *fc; for(fc=field_checks;fc;fc=fc->next) if(ci_eq(fc->cls,current_class)) type_env_set(&types,fc->field,fc->type); } { const char *declared_return=type_return_check_for(return_checks,full_name); if(declared_return&&!claro_is_type_word(declared_return)){ printf("%s:%d: %s %s declares an unknown return type %s. Use a Claro type such as NUMBER, TEXT, YESNO, LIST, or MAP.\n",path,line_no,strchr(full_name,'.')?"Method":"Function",full_name,declared_return); errs++; } else if(declared_return) snprintf(current_func,sizeof(current_func),"%s",full_name); } }
else if(!strcmp(up,"IF")){ if(in_teach){ if(nested_blocks<64){ branch_returned[nested_blocks]=0; branch_has_else[nested_blocks]=0; } nested_blocks++; } } else if(!strcmp(up,"IF")){ if(in_teach){ if(nested_blocks<64){ branch_returned[nested_blocks]=0; branch_has_else[nested_blocks]=0; } nested_blocks++; } }
@@ -0,0 +1,3 @@
CLASS
HAS score NUMBER
END
+3
View File
@@ -37,6 +37,9 @@ EXPECTED = {
"tests/typecheck_missing_function_name_bad.claro": [ "tests/typecheck_missing_function_name_bad.claro": [
"tests/typecheck_missing_function_name_bad.claro:1: TEACH needs a function name. Add a name after TEACH, such as TEACH greet.", "tests/typecheck_missing_function_name_bad.claro:1: TEACH needs a function name. Add a name after TEACH, such as TEACH greet.",
], ],
"tests/typecheck_missing_class_name_bad.claro": [
"tests/typecheck_missing_class_name_bad.claro:1: CLASS needs a class name. Add a name after CLASS, such as CLASS Player.",
],
"tests/typecheck_duplicate_object_bad.claro": [ "tests/typecheck_duplicate_object_bad.claro": [
"tests/typecheck_duplicate_object_bad.claro:6: Object player is created more than once. Give each object a different name.", "tests/typecheck_duplicate_object_bad.claro:6: Object player is created more than once. Give each object a different name.",
], ],