Repository: facebook/react
Pull request: [compiler] Add support for diagnostic hints


Hints are meant as additional information to present to the developer about an error. The first use-case here is for the suggestion to name refs with "-Ref" if we encounter a mutation that looks like it might be a ref. The original error printing used a second error detail which printed the source code twice, a hint with just extra text is less noisy.


Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts
patch: @@ -58,11 +58,15 @@ export type CompilerDiagnosticDetail =
   /**
    * A/the source of the error
    */
-  {
-    kind: 'error';
-    loc: SourceLocation | null;
-    message: string;
-  };
+  | {
+      kind: 'error';
+      loc: SourceLocation | null;
+      message: string;
+    }
+  | {
+      kind: 'hint';
+      message: string;
+    };
 
 export enum CompilerSuggestionOperation {
   InsertBefore,
@@ -134,7 +138,12 @@ export class CompilerDiagnostic {
   }
 
   primaryLocation(): SourceLocation | null {
-    return this.options.details.filter(d => d.kind === 'error')[0]?.loc ?? null;
+    const firstErrorDetail = this.options.details.filter(
+      d => d.kind === 'error',
+    )[0];
+    return firstErrorDetail != null && firstErrorDetail.kind === 'error'
+      ? firstErrorDetail.loc
+      : null;
   }
 
   printErrorMessage(source: string, options: PrintErrorMessageOptions): string {
@@ -167,9 +176,14 @@ export class CompilerDiagnostic {
           buffer.push(codeFrame);
           break;
         }
+        case 'hint': {
+          buffer.push('\n\n');
+          buffer.push(detail.message);
+          break;
+        }
         default: {
           assertExhaustive(
-            detail.kind,
+            detail,
             `Unexpected detail kind ${(detail as any).kind}`,
           );
         }

filename: compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
patch: @@ -471,8 +471,7 @@ function applySignature(
               effect.reason?.kind === 'AssignCurrentProperty'
             ) {
               diagnostic.withDetail({
-                kind: 'error',
-                loc: effect.value.loc,
+                kind: 'hint',
                 message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
               });
             }
@@ -1096,8 +1095,7 @@ function applyEffect(
             effect.reason?.kind === 'AssignCurrentProperty'
           ) {
             diagnostic.withDetail({
-              kind: 'error',
-              loc: effect.value.loc,
+              kind: 'hint',
               message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
             });
           }

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-assing-to-ref-current-in-render.expect.md
patch: @@ -30,13 +30,7 @@ Modifying a value returned from a hook is not allowed. Consider moving the modif
   7 | }
   8 |
 
-  3 | component Foo() {
-  4 |   const foo = useFoo();
-> 5 |   foo.current = true;
-    |   ^^^ Hint: If this value is a Ref (value returned by `useRef()`), rename the variable to end in "Ref".
-  6 |   return <div />;
-  7 | }
-  8 |
+Hint: If this value is a Ref (value returned by `useRef()`), rename the variable to end in "Ref".
 ```
           
       
\ No newline at end of file